Implemented extracting cash transfer data from PDF files and setting them as preselected values in TransferMoneyDialog

This commit is contained in:
dankito 2020-06-13 16:04:12 +02:00
parent 80680a16b6
commit 996204f54f
20 changed files with 297 additions and 13 deletions

View File

@ -27,6 +27,11 @@ ext {
luceneUtilsVersion = "0.5.1-SNAPSHOT"
textExtractorVersion = "0.5.1-SNAPSHOT"
textInfoExtractorVersion = "1.0.1-SNAPSHOT"
hbci4jVersion = '3.1.37'
@ -41,6 +46,8 @@ ext {
androidTargetSdkVersion = 28
fileChooserDialogVersion = "1.2.0-androidx"
androidUtilsVersion = '1.1.1-SNAPSHOT'
materialDrawerVersion = "8.0.1"

View File

@ -1,3 +1,7 @@
android.enableJetifier=true
android.useAndroidX=true
kotlin.code.style=official
kotlin.code.style=official
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx3072m

View File

@ -83,6 +83,10 @@ dependencies {
implementation project(':BankingPersistenceJson')
implementation project(':LuceneBankingPersistence')
implementation "net.dankito.text.extraction:itext2-text-extractor:$textExtractorVersion"
implementation "net.dankito.text.extraction:pdfbox-android-text-extractor:$textExtractorVersion"
implementation "com.github.clans:fab:$clansFloatingActionButtonVersion"
implementation "com.otaliastudios:autocomplete:$autocompleteVersion"
@ -90,6 +94,14 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinCoroutinesVersion"
implementation "net.dankito.filechooserdialog:filechooserdialog-android:$fileChooserDialogVersion", {
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib'
exclude group: 'com.android.support', module: 'appcompat-v7'
exclude group: 'com.android.support', module: 'design'
exclude group: 'com.android.support.constraint', module: 'constraint-layout'
exclude module: 'recyclerview-v7'
}
implementation "net.dankito.utils:android-utils:$androidUtilsVersion", {
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk7'
exclude group: 'com.android.support', module: 'appcompat-v7'

View File

@ -14,6 +14,8 @@ import net.dankito.banking.ui.android.activities.BaseActivity
import net.dankito.banking.ui.android.views.DrawerView
import net.dankito.banking.ui.android.views.MainActivityFloatingActionMenuButton
import net.dankito.banking.ui.presenter.BankingPresenter
import net.dankito.utils.android.permissions.IPermissionsService
import net.dankito.utils.android.permissions.PermissionsService
import javax.inject.Inject
@ -27,6 +29,8 @@ class MainActivity : BaseActivity() {
private lateinit var floatingActionMenuButton: MainActivityFloatingActionMenuButton
private val permissionsService: IPermissionsService = PermissionsService(this)
@Inject
protected lateinit var presenter: BankingPresenter
@ -73,7 +77,7 @@ class MainActivity : BaseActivity() {
}
val floatingActionMenu = findViewById<FloatingActionMenu>(R.id.floatingActionMenu)
floatingActionMenuButton = MainActivityFloatingActionMenuButton(floatingActionMenu, presenter)
floatingActionMenuButton = MainActivityFloatingActionMenuButton(floatingActionMenu, permissionsService, presenter)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {

View File

@ -19,6 +19,12 @@ import net.dankito.banking.util.BankIconFinder
import net.dankito.banking.util.IBankIconFinder
import net.dankito.banking.bankfinder.IBankFinder
import net.dankito.banking.bankfinder.LuceneBankFinder
import net.dankito.text.extraction.ITextExtractorRegistry
import net.dankito.text.extraction.TextExtractorRegistry
import net.dankito.text.extraction.info.invoice.IInvoiceDataExtractor
import net.dankito.text.extraction.info.invoice.InvoiceDataExtractor
import net.dankito.text.extraction.pdf.PdfBoxAndroidPdfTextExtractor
import net.dankito.text.extraction.pdf.iText2PdfTextExtractor
import net.dankito.utils.IThreadPool
import net.dankito.utils.ThreadPool
import net.dankito.utils.serialization.ISerializer
@ -78,10 +84,11 @@ class BankingModule(private val applicationContext: Context) {
@Singleton
fun provideBankingPresenter(bankingClientCreator: IBankingClientCreator, bankFinder: IBankFinder,
@Named(DatabaseFolderKey) databaseFolder: File, @Named(DataFolderKey) dataFolder: File,
persister: IBankingPersistence, bankIconFinder: IBankIconFinder, remitteeSearcher: IRemitteeSearcher,
router: IRouter, serializer: ISerializer, threadPool: IThreadPool) : BankingPresenter {
persister: IBankingPersistence, remitteeSearcher: IRemitteeSearcher, bankIconFinder: IBankIconFinder,
textExtractorRegistry: ITextExtractorRegistry, router: IRouter, invoiceDataExtractor: IInvoiceDataExtractor,
serializer: ISerializer, threadPool: IThreadPool) : BankingPresenter {
return BankingPresenter(bankingClientCreator, bankFinder, databaseFolder, dataFolder, persister,
remitteeSearcher, bankIconFinder, router, serializer, threadPool)
remitteeSearcher, bankIconFinder, textExtractorRegistry, router, invoiceDataExtractor, serializer, threadPool)
}
@Provides
@ -127,6 +134,22 @@ class BankingModule(private val applicationContext: Context) {
}
@Provides
@Singleton
fun provideTextExtractorRegistry(applicationContext: Context) : ITextExtractorRegistry {
// TODO: add PdfTypeDetector
return TextExtractorRegistry(listOf(
iText2PdfTextExtractor(), PdfBoxAndroidPdfTextExtractor(applicationContext)
))
}
@Provides
@Singleton
fun provideInvoiceDataExtractor() : IInvoiceDataExtractor {
return InvoiceDataExtractor()
}
@Provides
@Singleton
fun provideWebClient() : IWebClient {

View File

@ -1,18 +1,36 @@
package net.dankito.banking.ui.android.views
import android.content.Context
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.FragmentActivity
import com.github.clans.fab.FloatingActionButton
import com.github.clans.fab.FloatingActionMenu
import kotlinx.android.synthetic.main.view_floating_action_button_main.view.*
import net.dankito.banking.ui.android.R
import net.dankito.banking.ui.model.moneytransfer.ExtractTransferMoneyDataFromPdfResult
import net.dankito.banking.ui.model.moneytransfer.ExtractTransferMoneyDataFromPdfResultType
import net.dankito.banking.ui.presenter.BankingPresenter
import net.dankito.filechooserdialog.FileChooserDialog
import net.dankito.filechooserdialog.model.FileChooserDialogConfig
import net.dankito.utils.android.extensions.asActivity
import net.dankito.utils.android.permissions.IPermissionsService
import java.io.File
open class MainActivityFloatingActionMenuButton(floatingActionMenu: FloatingActionMenu, protected val presenter: BankingPresenter)
: FloatingActionMenuButton(floatingActionMenu) {
open class MainActivityFloatingActionMenuButton(
floatingActionMenu: FloatingActionMenu,
protected val permissionsService: IPermissionsService,
protected val presenter: BankingPresenter
) : FloatingActionMenuButton(floatingActionMenu) {
protected lateinit var fabTransferMoney: FloatingActionButton
protected lateinit var fabTransferMoneyFromPdf: FloatingActionButton
protected var lastSelectedFolder: File? = null
init {
setupButtons(floatingActionMenu)
@ -32,16 +50,59 @@ open class MainActivityFloatingActionMenuButton(floatingActionMenu: FloatingActi
}
fabTransferMoney = floatingActionMenu.fabTransferMoney
fabTransferMoneyFromPdf = floatingActionMenu.fabTransferMoneyFromPdf
fabTransferMoney.setOnClickListener {
executeAndCloseMenu { presenter.showTransferMoneyDialog() }
}
fabTransferMoneyFromPdf.setOnClickListener {
executeAndCloseMenu { transferMoneyWithDataFromPdf() }
}
}
}
protected open fun checkIfThereAreAccountsThatCanTransferMoney() {
fabTransferMoney.isEnabled = presenter.hasBankAccountsSupportTransferringMoney
fabTransferMoneyFromPdf.isEnabled = presenter.hasBankAccountsSupportTransferringMoney
}
protected open fun transferMoneyWithDataFromPdf() {
(floatingActionMenu.context.asActivity() as? FragmentActivity)?.let { activity ->
val config = FileChooserDialogConfig(listOf("*.pdf"), lastSelectedFolder)
FileChooserDialog().showOpenSingleFileDialog(activity, permissionsService, config) { _, selectedFile ->
selectedFile?.let {
lastSelectedFolder = selectedFile.parentFile
val result = presenter.transferMoneyWithDataFromPdf(selectedFile)
if (result.type != ExtractTransferMoneyDataFromPdfResultType.Success) {
showTransferMoneyWithDataFromPdfError(activity, selectedFile, result)
}
}
}
}
}
protected open fun showTransferMoneyWithDataFromPdfError(context: Context, pdfFile: File, result: ExtractTransferMoneyDataFromPdfResult) {
val errorMessage = when (result.type) {
ExtractTransferMoneyDataFromPdfResultType.NotASearchablePdf ->
context.getString(R.string.transfer_money_from_pdf_error_message_not_a_searchable_pdf, pdfFile.absolutePath)
ExtractTransferMoneyDataFromPdfResultType.CouldNotExtractText ->
context.getString(R.string.transfer_money_from_pdf_error_message_could_not_extract_text, pdfFile.absolutePath, result.error)
ExtractTransferMoneyDataFromPdfResultType.CouldNotExtractInvoiceDataFromExtractedText ->
context.getString(R.string.transfer_money_from_pdf_error_message_could_not_extract_invoice_data, pdfFile.absolutePath, result.error)
ExtractTransferMoneyDataFromPdfResultType.Success -> "" // will never come to this
}
AlertDialog.Builder(context)
.setMessage(errorMessage)
.setPositiveButton(android.R.string.ok) { dialog, _ -> dialog.dismiss() }
.show()
}
}

View File

@ -17,6 +17,14 @@
fab:menu_animationDelayPerItem="50">
<com.github.clans.fab.FloatingActionButton
android:id="@+id/fabTransferMoneyFromPdf"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/MenuButtonsStyle"
fab:fab_label="@string/floating_action_menu_transfer_money_from_pdf"
/>
<com.github.clans.fab.FloatingActionButton
android:id="@+id/fabTransferMoney"
android:layout_width="wrap_content"

View File

@ -23,6 +23,7 @@
<string name="floating_action_menu_add_account">Konto</string>
<string name="floating_action_menu_transfer_money">Überweisung</string>
<string name="floating_action_menu_transfer_money_from_pdf">Überweisung aus PDF</string>
<string name="menu_home">Umsätze</string>
@ -60,6 +61,10 @@
<string name="dialog_transfer_money_message_transfer_successful">%1$s %2$s wurden erfolgreich an %3$s überwiesen.</string>
<string name="dialog_transfer_money_message_transfer_failed">Konnte nicht %1$s %2$s an %3$s überweisen.\n\nFehlermeldung Ihrer Bank:\n\n%4$s</string>
<string name="transfer_money_from_pdf_error_message_not_a_searchable_pdf">Konnte Text nicht aus Datei "%1$s" extrahieren. Enthält sie auch Text oder nur Bilder?</string>
<string name="transfer_money_from_pdf_error_message_could_not_extract_text">Text konnte nicht aus Datei "%1$s" extrahiert werden:\n\n%2$s</string>
<string name="transfer_money_from_pdf_error_message_could_not_extract_invoice_data">Überweisungsdaten konnten aus der Datei "%1$s" nicht ausgelesen werden:\n\n%2$s</string>
<string name="view_tan_image_size_controls_size">Größe:</string>
<string name="view_tan_image_size_controls_decrease_size">-</string>
<string name="view_tan_image_size_controls_increase_size">+</string>

View File

@ -23,6 +23,7 @@
<string name="floating_action_menu_add_account">Account</string>
<string name="floating_action_menu_transfer_money">Transfer money</string>
<string name="floating_action_menu_transfer_money_from_pdf">Transfer money from PDF</string>
<string name="menu_home">Home</string>
@ -60,6 +61,10 @@
<string name="dialog_transfer_money_message_transfer_successful">Successfully transferred %1$s %2$s to %3$s.</string>
<string name="dialog_transfer_money_message_transfer_failed">Could not transfer %1$s %2$s to %3$s.\n\nError message from your bank:\n\n%4$s</string>
<string name="transfer_money_from_pdf_error_message_not_a_searchable_pdf">File "%1$s" is not a searchable PDF. Could therefore not extract text from it.</string>
<string name="transfer_money_from_pdf_error_message_could_not_extract_text">Could not extract text from file "%1$s":\n\n%2$s</string>
<string name="transfer_money_from_pdf_error_message_could_not_extract_invoice_data">Could not extract cash transfer data from file "%1$s":\n\n%2$s</string>
<string name="view_tan_image_size_controls_size">Size:</string>
<string name="view_tan_image_size_controls_decrease_size">-</string>
<string name="view_tan_image_size_controls_increase_size">+</string>

View File

@ -1,7 +1,7 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<style name="AppTheme" parent="FileChooserDialogAppThemeLight">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>

View File

@ -24,8 +24,18 @@ dependencies {
implementation project(':fints4kBankingClient')
implementation project(':hbci4jBankingClient')
implementation project(':fints4k')
implementation project(':fints4k-jvm')
implementation project(':LuceneBankingPersistence')
implementation "net.dankito.text.extraction:poppler-text-extractor:$textExtractorVersion"
implementation "net.dankito.text.extraction:pdfbox-text-extractor:$textExtractorVersion"
implementation "net.dankito.text.extraction:itext2-text-extractor:$textExtractorVersion"
implementation "net.dankito.text.extraction:tesseract4-commandline-text-extractor:$textExtractorVersion"
implementation "net.dankito.text.extraction:tika-text-extractor:$textExtractorVersion"
implementation "ch.qos.logback:logback-classic:$logbackVersion"
}

View File

@ -12,6 +12,12 @@ import net.dankito.banking.util.BankIconFinder
import net.dankito.banking.bankfinder.LuceneBankFinder
import net.dankito.banking.persistence.LuceneBankingPersistence
import net.dankito.banking.search.LuceneRemitteeSearcher
import net.dankito.text.extraction.TextExtractorRegistry
import net.dankito.text.extraction.TikaTextExtractor
import net.dankito.text.extraction.image.Tesseract4CommandlineImageTextExtractor
import net.dankito.text.extraction.image.model.OcrLanguage
import net.dankito.text.extraction.image.model.TesseractConfig
import net.dankito.text.extraction.pdf.*
import net.dankito.utils.web.client.OkHttpWebClient
import tornadofx.*
import tornadofx.FX.Companion.messages
@ -26,11 +32,20 @@ class MainWindow : View(messages["application.title"]) {
private val indexFolder = File(dataFolder, "index")
private val tesseractTextExtractor = Tesseract4CommandlineImageTextExtractor(TesseractConfig(listOf(OcrLanguage.English, OcrLanguage.German)))
private val textExtractorRegistry = TextExtractorRegistry(pdffontsPdfTypeDetector(), listOf(
pdfToTextPdfTextExtractor(), PdfBoxPdfTextExtractor(), iText2PdfTextExtractor(),
ImageOnlyPdfTextExtractor(tesseractTextExtractor, pdfimagesImagesFromPdfExtractor()),
tesseractTextExtractor, TikaTextExtractor()
))
private val presenter = BankingPresenter(fints4kBankingClientCreator(OkHttpWebClient(), Base64ServiceJava8()),
LuceneBankFinder(indexFolder), databaseFolder, dataFolder, LuceneBankingPersistence(indexFolder, databaseFolder),
LuceneRemitteeSearcher(indexFolder), BankIconFinder(), RouterJavaFx())
LuceneRemitteeSearcher(indexFolder), BankIconFinder(), textExtractorRegistry, RouterJavaFx())
// private val presenter = BankingPresenter(hbci4jBankingClientCreator(), LuceneBankFinder(indexFolder), databaseFolder,
// dataFolder, LuceneBankingPersistence(indexFolder, databaseFolder), LuceneRemitteeSearcher(indexFolder), BankIconFinder(), RouterJavaFx())
// dataFolder, LuceneBankingPersistence(indexFolder, databaseFolder), LuceneRemitteeSearcher(indexFolder),
// BankIconFinder(), textExtractorRegistry, RouterJavaFx())

View File

@ -4,15 +4,22 @@ import javafx.beans.property.SimpleBooleanProperty
import javafx.scene.input.KeyCode
import javafx.scene.input.KeyCodeCombination
import javafx.scene.input.KeyCombination
import javafx.stage.FileChooser
import net.dankito.banking.ui.model.moneytransfer.ExtractTransferMoneyDataFromPdfResult
import net.dankito.banking.ui.model.moneytransfer.ExtractTransferMoneyDataFromPdfResultType
import net.dankito.banking.ui.presenter.BankingPresenter
import net.dankito.utils.javafx.ui.dialogs.JavaFXDialogService
import net.dankito.utils.javafx.ui.extensions.fixedHeight
import tornadofx.*
import java.io.File
open class MainMenuBar(protected val presenter: BankingPresenter) : View() {
protected val areAccountsThatCanTransferMoneyAdded = SimpleBooleanProperty()
protected var lastSelectedFolder: File? = null
init {
presenter.addAccountsChangedListener {
@ -35,11 +42,19 @@ open class MainMenuBar(protected val presenter: BankingPresenter) : View() {
action { presenter.showAddAccountDialog() }
}
separator()
item(messages["main.window.menu.file.new.cash.transfer"], KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN)) {
enableWhen(areAccountsThatCanTransferMoneyAdded)
action { presenter.showTransferMoneyDialog() }
}
item(messages["main.window.menu.file.new.cash.transfer.from.pdf"], KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN, KeyCodeCombination.SHIFT_DOWN)) {
enableWhen(areAccountsThatCanTransferMoneyAdded)
action { transferMoneyWithDataFromPdf() }
}
}
separator()
@ -55,4 +70,34 @@ open class MainMenuBar(protected val presenter: BankingPresenter) : View() {
areAccountsThatCanTransferMoneyAdded.value = presenter.hasBankAccountsSupportTransferringMoney
}
protected open fun transferMoneyWithDataFromPdf() {
val fileChooser = FileChooser()
fileChooser.initialDirectory = lastSelectedFolder
fileChooser.extensionFilters.add(FileChooser.ExtensionFilter("PDFs (*.pdf)", "*.pdf"))
fileChooser.showOpenDialog(currentStage)?.let { pdfFile ->
lastSelectedFolder = pdfFile.parentFile
val result = presenter.transferMoneyWithDataFromPdf(pdfFile)
if (result.type != ExtractTransferMoneyDataFromPdfResultType.Success) {
showTransferMoneyWithDataFromPdfError(pdfFile, result)
}
}
}
protected open fun showTransferMoneyWithDataFromPdfError(pdfFile: File, result: ExtractTransferMoneyDataFromPdfResult) {
val errorMessageKey = when (result.type) {
ExtractTransferMoneyDataFromPdfResultType.NotASearchablePdf -> "transfer.money.from.pdf.error.message.not.a.searchable.pdf"
ExtractTransferMoneyDataFromPdfResultType.CouldNotExtractText -> "transfer.money.from.pdf.error.message.could.not.extract.text"
ExtractTransferMoneyDataFromPdfResultType.CouldNotExtractInvoiceDataFromExtractedText -> "transfer.money.from.pdf.error.message.could.not.extract.invoice.data"
ExtractTransferMoneyDataFromPdfResultType.Success -> "" // will never come to this
}
val errorMessage = String.format(messages[errorMessageKey], pdfFile.absolutePath)
JavaFXDialogService().showErrorMessage(errorMessage, exception = result.error)
}
}

View File

@ -13,6 +13,7 @@ main.window.menu.file=File
main.window.menu.file.new=New...
main.window.menu.file.new.account=Account
main.window.menu.file.new.cash.transfer=Cash transfer
main.window.menu.file.new.cash.transfer.from.pdf=Cash transfer from PDF
main.window.menu.file.quit=Quit
@ -71,4 +72,8 @@ transfer.money.dialog.transfer.money.label=Transfer
transfer.money.dialog.bank.name.will.be.entered.automatically=Will be entered automatically
transfer.money.dialog.bank.not.found.for.iban=No bank found for this IBAN
transfer.money.dialog.message.transfer.cash.success=Successfully transferred %.02f %s to %s
transfer.money.dialog.message.transfer.cash.error=Could not transfer %.02f %s to %s.\r\n\r\nError message from your bank:\r\n\r\n%s
transfer.money.dialog.message.transfer.cash.error=Could not transfer %.02f %s to %s.\r\n\r\nError message from your bank:\r\n\r\n%s
transfer.money.from.pdf.error.message.not.a.searchable.pdf=File "%1$s" is not a searchable PDF. Could therefore not extract text from it.
transfer.money.from.pdf.error.message.could.not.extract.text=Could not extract text from file "%1$s".
transfer.money.from.pdf.error.message.could.not.extract.invoice.data=Could not extract cash transfer data from file "%1$s".

View File

@ -13,6 +13,7 @@ main.window.menu.file=Datei
main.window.menu.file.new=Neu...
main.window.menu.file.new.account=Konto
main.window.menu.file.new.cash.transfer=Überweisung
main.window.menu.file.new.cash.transfer.from.pdf=Überweisung aus PDF
main.window.menu.file.quit=Beenden
@ -71,4 +72,8 @@ transfer.money.dialog.transfer.money.label=Überweisen
transfer.money.dialog.bank.name.will.be.entered.automatically=Wird automatisch eingetragen
transfer.money.dialog.bank.not.found.for.iban=Für diese IBAN wurde keine Bank gefunden
transfer.money.dialog.message.transfer.cash.success=%.02f %s erfolgreich an %s überwiesen.
transfer.money.dialog.message.transfer.cash.error=%.02f %s konnten nicht an %s überwiesen werden.\r\n\r\nFehlermeldung Ihrer Bank:\r\n\r\n%s
transfer.money.dialog.message.transfer.cash.error=%.02f %s konnten nicht an %s überwiesen werden.\r\n\r\nFehlermeldung Ihrer Bank:\r\n\r\n%s
transfer.money.from.pdf.error.message.not.a.searchable.pdf=Konnte Text nicht aus Datei "%1$s" extrahieren. Enthält sie auch Text oder nur Bilder?
transfer.money.from.pdf.error.message.could.not.extract.text=Text konnte nicht aus Datei "%1$s" extrahiert werden.
transfer.money.from.pdf.error.message.could.not.extract.invoice.data=Überweisungsdaten konnten aus der Datei "%1$s" nicht ausgelesen werden.

View File

@ -13,11 +13,22 @@ compileTestKotlin {
}
repositories {
// for security issues fixed version of iText 2 from JasperReports
maven {
url "http://jaspersoft.jfrog.io/jaspersoft/third-party-ce-artifacts"
}
}
dependencies {
api "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
api "net.dankito.utils:java-utils:$javaUtilsVersion"
api "net.dankito.text.extraction:text-extractor-common:$textExtractorVersion"
api "net.dankito.text.extraction:text-info-extractor:$textInfoExtractorVersion"
implementation "net.dankito.utils:favicon-finder:1.0.0-SNAPSHOT"
implementation "org.jsoup:jsoup:1.13.1"

View File

@ -0,0 +1,13 @@
package net.dankito.banking.ui.model.moneytransfer
open class ExtractTransferMoneyDataFromPdfResult(
val type: ExtractTransferMoneyDataFromPdfResultType,
val error: Exception? = null
) {
override fun toString(): String {
return type.toString()
}
}

View File

@ -0,0 +1,14 @@
package net.dankito.banking.ui.model.moneytransfer
enum class ExtractTransferMoneyDataFromPdfResultType {
Success,
NotASearchablePdf,
CouldNotExtractText,
CouldNotExtractInvoiceDataFromExtractedText
}

View File

@ -19,8 +19,14 @@ import net.dankito.banking.bankfinder.IBankFinder
import net.dankito.banking.bankfinder.BankInfo
import net.dankito.banking.search.IRemitteeSearcher
import net.dankito.banking.search.Remittee
import net.dankito.banking.ui.model.moneytransfer.ExtractTransferMoneyDataFromPdfResult
import net.dankito.banking.ui.model.moneytransfer.ExtractTransferMoneyDataFromPdfResultType
import net.dankito.banking.ui.model.parameters.GetTransactionsParameter
import net.dankito.banking.ui.model.settings.AppSettings
import net.dankito.text.extraction.ITextExtractorRegistry
import net.dankito.text.extraction.info.invoice.IInvoiceDataExtractor
import net.dankito.text.extraction.info.invoice.InvoiceDataExtractor
import net.dankito.text.extraction.model.ErrorType
import net.dankito.utils.IThreadPool
import net.dankito.utils.ThreadPool
import net.dankito.utils.extensions.containsExactly
@ -45,7 +51,9 @@ open class BankingPresenter(
protected val persister: IBankingPersistence,
protected val remitteeSearcher: IRemitteeSearcher,
protected val bankIconFinder: IBankIconFinder,
protected val textExtractorRegistry: ITextExtractorRegistry,
protected val router: IRouter,
protected val invoiceDataExtractor: IInvoiceDataExtractor = InvoiceDataExtractor(),
protected val serializer: ISerializer = JacksonJsonSerializer(),
protected val threadPool: IThreadPool = ThreadPool()
) {
@ -380,6 +388,35 @@ open class BankingPresenter(
}
}
open fun transferMoneyWithDataFromPdf(pdf: File): ExtractTransferMoneyDataFromPdfResult {
val extractionResult = textExtractorRegistry.extractTextWithBestExtractorForFile(pdf)
if (extractionResult.couldExtractText == false || extractionResult.text == null) {
val resultType = if (extractionResult.error?.type == ErrorType.NoExtractorFoundForFileType) ExtractTransferMoneyDataFromPdfResultType.NotASearchablePdf
else ExtractTransferMoneyDataFromPdfResultType.CouldNotExtractText
return ExtractTransferMoneyDataFromPdfResult(resultType, extractionResult.error?.exception)
}
else {
extractionResult.text?.let { extractedText ->
val invoiceData = invoiceDataExtractor.extractInvoiceData(extractedText)
if (invoiceData.potentialTotalAmount != null || invoiceData.potentialIban != null) { // if at least an amount or IBAN could get extracted
val transferMoneyData = TransferMoneyData("",
invoiceData.potentialIban ?: "",
invoiceData.potentialBic ?: "",
invoiceData.potentialTotalAmount?.amount ?: BigDecimal.ZERO, "")
showTransferMoneyDialog(null, transferMoneyData)
}
else {
return ExtractTransferMoneyDataFromPdfResult(
ExtractTransferMoneyDataFromPdfResultType.CouldNotExtractInvoiceDataFromExtractedText, invoiceData.error)
}
}
}
return ExtractTransferMoneyDataFromPdfResult(ExtractTransferMoneyDataFromPdfResultType.Success)
}
open fun findUniqueBankForIbanAsync(iban: String, callback: (BankInfo?) -> Unit) {
threadPool.runAsync {

View File

@ -15,5 +15,5 @@ compileTestKotlin {
dependencies {
api project(':BankingUiCommon')
implementation project(':fints4k')
api project(':fints4k')
}