Commit 9f769f0b authored by seykron's avatar seykron

Closes #25: implements command to cancel bill

parent 31a17684
package be.rlab.domino.application.bot
import be.rlab.domino.domain.TreasuryService
import be.rlab.domino.domain.model.Bill
import be.rlab.tehanu.domain.Command
import be.rlab.tehanu.domain.MessageContext
import be.rlab.tehanu.domain.model.ChatType
import be.rlab.tehanu.domain.model.TextMessage
import org.joda.time.format.DateTimeFormat
/** Cancels an existing bill.
*/
class CancelBill(
override val name: String,
override val scope: List<ChatType>,
override val timeout: Long,
private val treasuryService: TreasuryService
) : Command {
data class CancelBillInput(
val billId: Int
)
override fun handle(
context: MessageContext,
message: TextMessage
): MessageContext {
val pendingBills: List<Bill> = treasuryService.listPendingBills()
val billsMessage: String = pendingBills.joinToString("\n") { bill ->
val dueDate: String? = bill.dueDate?.toString(DateTimeFormat.forPattern("dd/MM/YYYY"))
"[${bill.shortId()}] ${bill.creditor().userName}: ${bill.credit.debt} (vence $dueDate)"
}
return context.parseInput(message.text,
"Decime qué factura querés cancelar:\n\n$billsMessage\n\n" +
"""
bill_id: IdDeLaFactura
""".trimIndent()
) { billInfo: CancelBillInput ->
val bill: Bill = require(pendingBills.find { pendingBill ->
pendingBill.shortId() == billInfo.billId
}, "La factura con id ${billInfo.billId} no existe.")
treasuryService.cancelBill(bill)
answer("listo, cancelé la factura")
}
}
}
package be.rlab.domino.config
import be.rlab.tehanu.domain.model.ChatType
import be.rlab.domino.application.bot.*
import be.rlab.tehanu.domain.model.ChatType
import org.springframework.context.support.beans
object CommandBeans {
......@@ -123,6 +123,14 @@ object CommandBeans {
memory = ref()
)
}
bean {
CancelBill(
name = "/cancel_bill",
scope = listOf(ChatType.GROUP),
timeout = 120000,
treasuryService = ref()
)
}
bean {
Bills(
name = "/bills",
......
......@@ -3,7 +3,6 @@ package be.rlab.domino.domain
import be.rlab.domino.domain.model.Account
import be.rlab.domino.domain.model.Balance
import be.rlab.domino.domain.model.Bill
import be.rlab.domino.domain.model.BlockSignature.Companion.INITIAL_SIGNATURE
import be.rlab.domino.domain.model.Transaction
import be.rlab.domino.domain.persistence.BalanceDAO
import be.rlab.domino.domain.persistence.BillDAO
......
......@@ -159,7 +159,10 @@ class TreasuryService(
return billDAO.findByPeriod(treasuryAccount().id, from, to)
}
private fun cancelBill(bill: Bill) {
/** Cancels a bill.
* It generates a credit note and invalidates the underlying debt and credit.
*/
fun cancelBill(bill: Bill) {
val service: Agent = bill.creditor()
val invalidDebt: Transaction = transactionService.invalidate(
......
CREATE TABLE IF NOT EXISTS `treasury_credit_notes` (
`id` binary(16) NOT NULL,
`creation_date` timestamp(6) NOT NULL,
`signature` binary(16) NOT NULL,
`account` binary(16) NOT NULL,
`description` varchar(255) NOT NULL,
`data` text NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_treasury_credit_notes_signature_id` (`signature`),
KEY `fk_treasury_credit_notes_account_id` (`account`),
KEY `treasury_credits_creation_date` (`creation_date`),
CONSTRAINT `fk_treasury_credit_notes_account_id` FOREIGN KEY (`account`) REFERENCES `treasury_accounts` (`id`),
CONSTRAINT `fk_treasury_credit_notes_signature_id` FOREIGN KEY (`signature`) REFERENCES `treasury_signatures` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
ALTER TABLE treasury_transactions ADD COLUMN `is_invalid` tinyint(1) NOT NULL DEFAULT 0;
......@@ -76,9 +76,13 @@
<td>${balance.income}</td>
</tr>
<tr>
<td>Balance Anterior</td>
<td>Balance anterior</td>
<td>${balance.previousBalance}</td>
</tr>
<tr>
<td>Balance del mes</td>
<td>${balance.monthlyBalance}</td>
</tr>
<tr class="blue-grey lighten-5">
<td>Balance total</td>
<td>${balance.totalBalance}</td>
......
package be.rlab.domino.application
import be.rlab.domino.application.model.BalanceDTO
import be.rlab.domino.domain.model.*
import be.rlab.domino.domain.model.Value.Companion.valueOf
import org.joda.time.DateTime
import org.junit.Test
class BalanceFactoryTest {
@Test
fun create() {
val previousBalance: Balance = TestBalance(
transactions = listOf(income(100.0), debt(200.0)),
bills = emptyList()
).new()
val balance: Balance = TestBalance(
transactions = listOf(
debt(300.0), debt(150.0), income(500.0), income(200.0)
),
bills = listOf(
TestBill(
debt = debt(200.0).invalidate(),
description = "factura de luz 1"
).new(),
TestBill(
debt = debt(300.0),
credit = TestCredit(
account = TestAccount(owner = TestAgent(userName = "foo").new()).new(),
income = income(300.0)
).new().cancel(valueOf(Currency.ARS, 50.0)),
description = "factura de luz 2",
dueDate = DateTime.parse("2019-07-30")
).new(),
TestBill(
debt = debt(150.0),
credit = TestCredit(
account = TestAccount(owner = TestAgent(userName = "bar").new()).new(),
income = income(150.0)
).new(),
description = "factura de agua",
dueDate = DateTime.parse("2019-07-28")
).new()
)
).new()
val factory = BalanceFactory()
val balanceDto: BalanceDTO = factory.create(previousBalance, balance)
assert(balanceDto.previousBalance == valueOf(Currency.ARS, -100.0))
assert(balanceDto.monthlyBalance == valueOf(Currency.ARS, 250.0))
assert(balanceDto.debtsPending == valueOf(Currency.ARS, 400.0))
assert(balanceDto.debtsTotal == valueOf(Currency.ARS, 450.0))
assert(balanceDto.income == valueOf(Currency.ARS, 700.0))
assert(balanceDto.totalBalance == valueOf(Currency.ARS, 150.0))
assert(balanceDto.bills.size == 2)
assert(balanceDto.bills[0].pending)
assert(balanceDto.bills[0].debt == valueOf(Currency.ARS, 250.0))
assert(balanceDto.bills[0].dueDate == DateTime.parse("2019-07-30"))
assert(balanceDto.bills[0].total == valueOf(Currency.ARS, 300.0))
assert(balanceDto.bills[0].serviceName == "foo")
assert(balanceDto.bills[1].pending)
assert(balanceDto.bills[1].debt == valueOf(Currency.ARS, 150.0))
assert(balanceDto.bills[1].dueDate == DateTime.parse("2019-07-28"))
assert(balanceDto.bills[1].total == valueOf(Currency.ARS, 150.0))
assert(balanceDto.bills[1].serviceName == "bar")
}
private fun debt(value: Double): Transaction {
return TestTransaction(
type = TransactionType.DEBT,
value = valueOf(Currency.ARS, value)
).new()
}
private fun income(value: Double): Transaction {
return TestTransaction(
type = TransactionType.DEPOSIT,
value = valueOf(Currency.ARS, -value)
).new()
}
}
\ No newline at end of file
package be.rlab.domino.domain.model
import be.rlab.domino.domain.model.BlockSignature.Companion.INITIAL_SIGNATURE
import org.joda.time.DateTime
data class TestBalance(
val account: Account = TestAccount().new(),
val description: String = "Balance del mes",
val from: DateTime = DateTime.now().minusDays(60),
val to: DateTime = DateTime.now().minusDays(30),
val transactions: List<Transaction> = emptyList(),
val bills: List<Bill> = emptyList()
) {
fun new(): Balance = Balance.new(
previousSignature = INITIAL_SIGNATURE,
account = account,
description = description,
from = from,
to = to,
transactions = transactions,
bills = bills
)
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment