Commit 83dff497 authored by seykron's avatar seykron

Issue #25: fixes cancel bill operation

parent f67ceb4b
......@@ -7,10 +7,7 @@ import be.rlab.domino.application.model.ServiceConfig
import be.rlab.domino.domain.AccountService
import be.rlab.domino.domain.AgentService
import be.rlab.domino.domain.TreasuryService
import be.rlab.domino.domain.model.Account
import be.rlab.domino.domain.model.Agent
import be.rlab.domino.domain.model.Currency
import be.rlab.domino.domain.model.Value
import be.rlab.domino.domain.model.*
import be.rlab.tehanu.domain.Command
import be.rlab.tehanu.domain.Memory
import be.rlab.tehanu.domain.MessageContext
......@@ -88,15 +85,24 @@ class AddBill(
"establecer los parámetros de configuración"
)
treasuryService.newBill(
val value: Value = Value.valueOf(billInfo.currency, billInfo.amount)
val newBill: Bill = treasuryService.newBill(
service = service,
description = billInfo.description,
value = Value.valueOf(billInfo.currency, billInfo.amount),
value = value,
dueDate = billInfo.dueDate,
replace = config.billingMode == BillingMode.SINGLE
)
answer("listo, la factura fue cargada en la cuenta del tesoro")
if (newBill.debt.value != value) {
answer(
"ya existe una factura anterior que está parcialmente paga, a la nueva " +
"factura le resté el monto que ya pagaste y quedó cargada con un monto de ${newBill.debt.value}"
)
} else {
answer("listo, la factura fue cargada en la cuenta del tesoro")
}
}
}
}
......@@ -69,15 +69,8 @@ class Balance(
Balance total: ${result.totalBalance}
""".trimIndent())
val debtsMessage: String = result.bills.joinToString("\n") { bill ->
val dueDate: String? = bill.dueDate?.toString(DateTimeFormat.forPattern("dd/MM/YYYY"))
val pending: String = if (bill.pending) "PENDIENTE" else "PAGADA"
"${bill.serviceName}: ${bill.debt} (vence $dueDate) - $pending"
}
context.talk("# Deudas\n$debtsMessage")
context.talk("# Balance actual de cuentas\n${accountsBalance()}")
context.talk("https://domino.rlab.be/balance?year=${date.year}&month=${date.monthOfYear}")
return context
}
......@@ -85,7 +78,7 @@ class Balance(
private fun accountsBalance(): String {
val accounts: List<Account> = accountService.findByGroup(treasuryConfig.group)
return accounts.joinToString("\n") { account ->
"@${account.owner.userName}: ${-account.debt}"
"${account.owner.userName}: ${-account.debt}"
}
}
}
......@@ -41,9 +41,12 @@ class CancelBill(
pendingBill.shortId() == billInfo.billId
}, "La factura con id ${billInfo.billId} no existe.")
treasuryService.cancelBill(bill)
answer("listo, cancelé la factura")
if (bill.credit.debt == -bill.credit.income.value) {
treasuryService.cancelBill(bill)
answer("listo, cancelé la factura")
} else {
answer("no podés cancelar una factura que ya está parcialmente paga")
}
}
}
}
......@@ -141,4 +141,8 @@ class AccountService(
accountDAO.saveOrUpdate(account)
}
fun saveOrUpdate(account: Account): Account {
return accountDAO.saveOrUpdate(account)
}
}
......@@ -77,26 +77,41 @@ class TreasuryService(
val chargedAccount: Account = treasuryAccount()
val creditorAccount: Account = accountService.findDefaultByAgent(service.id)
if (replace) {
billDAO.listPending(
val resolvedValue: Value = if (replace) {
val pendingBills: List<Bill> = billDAO.listPending(
accountId = chargedAccount.id,
agentId = service.id
).forEach { bill ->
cancelBill(bill)
).sortedBy { it.creationDate }
pendingBills.forEach { bill ->
if (!bill.partial()) {
cancelBill(bill)
} else {
billDAO.saveOrUpdate(bill.invalidate())
}
}
when {
pendingBills.isNotEmpty() && pendingBills.last().partial() ->
value - pendingBills.last().totalPaid()
else ->
value
}
} else {
value
}
val debt: Transaction = transactionService.newDebt(
account = chargedAccount,
account = treasuryAccount(),
description = "Nueva factura de: ${service.fullName}",
value = value
value = resolvedValue
)
val serviceCredit: Credit = transactionService.newCredit(
account = creditorAccount,
description = "Crédito por prestación de servicios",
debtor = chargedAccount.owner,
total = value,
total = resolvedValue,
dueDate = dueDate
)
......@@ -176,6 +191,12 @@ class TreasuryService(
)
)
val account: Account = accountService.findById(bill.debt.account.id)
// Updates the account consolidated balance.
accountService.saveOrUpdate(
account.income(bill.credit.debt)
)
billDAO.saveOrUpdate(bill.invalidate(
invalidDebt = invalidDebt,
invalidCredit = invalidCredit
......
......@@ -102,8 +102,8 @@ data class Bill(
* @return the cancelled bill.
*/
fun invalidate(
invalidDebt: Transaction,
invalidCredit: Credit
invalidDebt: Transaction = debt,
invalidCredit: Credit = credit
): Bill = copy(
credit = invalidCredit,
debt = invalidDebt,
......@@ -118,6 +118,13 @@ data class Bill(
*/
fun cancelled(): Boolean = debt.invalid
fun totalPaid(): Value = debt.value - credit.debt
/** Indicates whether this bill is partially paid.
* @return true if partially paid, false otherwise.
*/
fun partial(): Boolean = credit.payments.isNotEmpty()
override fun blockId(): String = buildSignature(
fields = listOf(credit.signature, debt.signature)
)
......
package be.rlab.domino.domain.persistence
import be.rlab.tehanu.util.ObjectMapperFactory
import be.rlab.domino.domain.model.Activity
import be.rlab.domino.util.SignatureUtils.calculateSignature
import be.rlab.domino.util.persistence.AbstractEntity
import be.rlab.domino.util.persistence.EntitySerialization.serialize
import be.rlab.tehanu.util.ObjectMapperFactory
import org.jetbrains.exposed.dao.EntityID
import org.jetbrains.exposed.dao.UUIDTable
import org.joda.time.DateTime
......@@ -51,8 +51,13 @@ abstract class ActivityEntity<T : Activity>(
return this
}
abstract fun initEntity(
entity: T
): T
override fun toDomainType(): T {
return verify(ObjectMapperFactory.snakeCaseMapper.readValue(data, type.java))
val entity: T = ObjectMapperFactory.snakeCaseMapper.readValue(data, type.java)
return verify(initEntity(entity))
}
private fun verify(activity: T): T {
......
package be.rlab.domino.domain.persistence
import be.rlab.domino.domain.model.Account
import be.rlab.domino.domain.model.Balance
import be.rlab.domino.util.persistence.AbstractEntityClass
import org.jetbrains.exposed.dao.EntityID
......@@ -27,4 +28,8 @@ class BalanceEntity(id: EntityID<UUID>) : ActivityEntity<Balance>(
to = source.to
return this
}
override fun initEntity(entity: Balance): Balance = entity.copy(
account = account.toDomainType()
)
}
......@@ -42,4 +42,10 @@ class BillEntity(id: EntityID<UUID>) : ActivityEntity<Bill>(
return this
}
override fun initEntity(
entity: Bill
): Bill = entity.copy(
account = account.toDomainType()
)
}
......@@ -13,6 +13,12 @@ class CreditNoteEntity(id: EntityID<UUID>) : ActivityEntity<CreditNote>(
type = CreditNote::class
) {
companion object : AbstractEntityClass<CreditNote, CreditNoteEntity>(CreditNotes)
override fun initEntity(
entity: CreditNote
): CreditNote = entity.copy(
account = account.toDomainType()
)
}
/** DAO to manage [CreditNote]s.
......
......@@ -13,6 +13,12 @@ class CreditEntity(id: EntityID<UUID>) : ActivityEntity<Credit>(
type = Credit::class
) {
companion object : AbstractEntityClass<Credit, CreditEntity>(Credits)
override fun initEntity(
entity: Credit
): Credit = entity.copy(
account = account.toDomainType()
)
}
/** DAO to manage [Credit]s.
......
......@@ -30,4 +30,10 @@ class TransactionEntity(id: EntityID<UUID>) : ActivityEntity<Transaction>(
return super.update(source)
}
override fun initEntity(
entity: Transaction
): Transaction = entity.copy(
account = account.toDomainType()
)
}
......@@ -56,11 +56,19 @@
<tr>
<td>
<table class="services striped">
<thead>
<tr>
<th>Servicio</th>
<th>Total</th>
<th>Pendiente</th>
</tr>
</thead>
<tbody>
<#list balance.bills as bill>
<tr>
<tr class="<#if bill.pending>red lighten-4</#if>">
<td>${bill.serviceName}</td>
<td>${bill.total}</td>
<td>${bill.debt}</td>
</tr>
</#list>
</tbody>
......
......@@ -190,6 +190,7 @@ class TreasuryServiceTest {
accountService = accountService
.findTreasuryAccount(chargedAccount)
.findDefaultByAgent(creditor.id, creditorAccount)
.findById(pendingBill.debt.account.id, pendingBill.debt.account)
.instance,
transactionService = transactionService
.invalidate(
......
......@@ -13,4 +13,10 @@ class TestActivityEntity(id: EntityID<UUID>) : ActivityEntity<TestActivity>(
type = TestActivity::class
) {
companion object : AbstractEntityClass<TestActivity, TestActivityEntity>(TestActivities)
override fun initEntity(
entity: TestActivity
): TestActivity = entity.copy(
account = account.toDomainType()
)
}
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