List.kt (6190B)
1 /* 2 * This file is part of LibEuFin. 3 * Copyright (C) 2025, 2026 Taler Systems S.A. 4 5 * LibEuFin is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU Affero General Public License as 7 * published by the Free Software Foundation; either version 3, or 8 * (at your option) any later version. 9 10 * LibEuFin is distributed in the hope that it will be useful, but 11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General 13 * Public License for more details. 14 15 * You should have received a copy of the GNU Affero General Public 16 * License along with LibEuFin; see the file COPYING. If not, see 17 * <http://www.gnu.org/licenses/> 18 */ 19 20 package tech.libeufin.nexus.cli 21 22 import com.github.ajalt.clikt.core.CliktCommand 23 import com.github.ajalt.clikt.core.Context 24 import com.github.ajalt.clikt.core.subcommands 25 import com.github.ajalt.clikt.parameters.arguments.* 26 import com.github.ajalt.clikt.parameters.groups.provideDelegate 27 import com.github.ajalt.clikt.parameters.options.convert 28 import com.github.ajalt.clikt.parameters.options.default 29 import com.github.ajalt.clikt.parameters.options.flag 30 import com.github.ajalt.clikt.parameters.options.option 31 import com.github.ajalt.clikt.parameters.types.* 32 import com.github.ajalt.mordant.terminal.* 33 import tech.libeufin.common.* 34 import tech.libeufin.nexus.* 35 import tech.libeufin.nexus.iso20022.* 36 import java.util.zip.* 37 import java.time.Instant 38 import java.io.* 39 40 private fun fmtPayto(payto: String): String { 41 try { 42 val parsed = Payto.parse(payto).expectIban() 43 return buildString { 44 append(parsed.iban.toString()) 45 if (parsed.bic != null) append(" ${parsed.bic}") 46 if (parsed.receiverName != null) append(" ${parsed.receiverName}") 47 } 48 } catch (e: Exception) { 49 return payto.removePrefix("payto://") 50 } 51 } 52 53 54 class ListIncoming: TalerCmd("incoming") { 55 override fun help(context: Context) = "List incoming transactions" 56 57 private val incomplete by option( 58 "--incomplete", 59 help = "Only list transactions that are incomplete", 60 ).flag() 61 62 63 override fun run() = cliCmd(logger) { 64 nexusConfig(config).withDb { db, cfg -> 65 val txs = db.list.incoming(incomplete) 66 for (tx in txs) { 67 println(buildString{ 68 if (tx.creditFee.isZero()) { 69 append("${tx.date} ${tx.id} ${tx.amount}\n") 70 } else { 71 append("${tx.date} ${tx.id} ${tx.amount}-${tx.creditFee}\n") 72 } 73 if (tx.debtor != null) { 74 append(" debtor: ${fmtPayto(tx.debtor)}\n") 75 } 76 if (tx.subject != null) { 77 append(" subject: ${tx.subject}\n") 78 } 79 if (tx.talerable != null) { 80 append(" talerable: ${tx.talerable}\n") 81 } 82 if (tx.bounced != null) { 83 append(" bounced: ${tx.bounced}\n") 84 } 85 }) 86 } 87 } 88 } 89 } 90 91 class ListOutgoing: TalerCmd("outgoing") { 92 override fun help(context: Context) = "List outgoing transactions" 93 94 override fun run() = cliCmd(logger) { 95 nexusConfig(config).withDb { db, cfg -> 96 val txs = db.list.outgoing() 97 for (tx in txs) { 98 println(buildString{ 99 append("${tx.date} ${tx.id} ${tx.amount}\n") 100 if (tx.creditor != null) { 101 append(" creditor: ${fmtPayto(tx.creditor)}\n") 102 } 103 append(" subject: ${tx.subject}\n") 104 if (tx.wtid != null) { 105 append(" talerable: ${tx.wtid} ${tx.exchangeBaseUrl}\n") 106 } 107 }) 108 } 109 } 110 } 111 } 112 113 class ListInitiated: TalerCmd("initiated") { 114 override fun help(context: Context) = "List initiated transactions" 115 116 private val awaitingAck by option( 117 "--ack", "--awaiting-ack", 118 help = "Only list transactions awaiting manual acknowledgement", 119 ).flag() 120 121 override fun run() = cliCmd(logger) { 122 nexusConfig(config).withDb { db, cfg -> 123 if (awaitingAck) { 124 val txs = db.list.initiatedAck() 125 for (tx in txs) { 126 println(buildString{ 127 append("${tx.date} ${tx.id} ${tx.amount}\n") 128 append(" creditor: ${fmtPayto(tx.creditor)}\n") 129 append(" subject: ${tx.subject}\n") 130 append(" ack: ${tx.dbId}") 131 append('\n') 132 }) 133 } 134 } else { 135 val txs = db.list.initiated() 136 for (tx in txs) { 137 println(buildString{ 138 append("${tx.date} ${tx.id} ${tx.amount}\n") 139 append(" creditor: ${fmtPayto(tx.creditor)}\n") 140 append(" subject: ${tx.subject}\n") 141 if (tx.batch != null) { 142 append(" batch: ${tx.batch}") 143 if (tx.batchOrder != null) 144 append(" ${tx.batchOrder}") 145 append('\n') 146 } 147 if (tx.submissionCounter > 0) { 148 append(" submission: ${tx.submissionTime} ${tx.submissionCounter}\n") 149 } 150 append(" status: ${tx.status}") 151 if (tx.msg != null) { 152 append(" ${tx.msg}") 153 } 154 append('\n') 155 }) 156 } 157 } 158 } 159 } 160 } 161 162 163 class ListCmd: CliktCommand("list") { 164 override fun help(context: Context) = "List nexus transactions" 165 166 init { 167 subcommands(ListIncoming(), ListOutgoing(), ListInitiated()) 168 } 169 170 override fun run() = Unit 171 }