Skip to content

Commit

Permalink
Merge pull request #245 from eurofurence/issue-244-write-dues-reason
Browse files Browse the repository at this point in the history
fill reason field on dues transactions
  • Loading branch information
Jumpy-Squirrel authored Dec 10, 2024
2 parents 0c4f56f + dfb2f81 commit 2692e64
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 9 deletions.
1 change: 1 addition & 0 deletions internal/repository/paymentservice/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ type Transaction struct {
DueDate string `json:"due_date"`
CreationDate time.Time `json:"creation_date"`
StatusHistory []StatusHistory `json:"status_history"`
Reason string `json:"reason"`
}

type TransactionResponse struct {
Expand Down
56 changes: 47 additions & 9 deletions internal/service/attendeesrv/dues.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package attendeesrv

import (
"context"
"encoding/json"
"errors"
"fmt"
aulogging "github.com/StephanHCB/go-autumn-logging"
"github.com/eurofurence/reg-attendee-service/internal/api/v1/attendee"
"github.com/eurofurence/reg-attendee-service/internal/api/v1/status"
"github.com/eurofurence/reg-attendee-service/internal/entity"
"github.com/eurofurence/reg-attendee-service/internal/repository/config"
Expand All @@ -27,12 +29,12 @@ func (s *AttendeeServiceImplData) UpdateDuesTransactions(ctx context.Context, at

updated := false
if newStatus == status.New || newStatus == status.Deleted || newStatus == status.Waiting {
updated, err = s.compensateAllDues(ctx, attendee, newStatus, transactionHistory)
updated, err = s.compensateAllDues(ctx, attendee, adminInfo, newStatus, transactionHistory)
if err != nil {
return transactionHistory, adminInfo, err
}
} else if newStatus == status.Cancelled {
updated, err = s.compensateUnpaidDuesOnCancel(ctx, attendee, transactionHistory)
updated, err = s.compensateUnpaidDuesOnCancel(ctx, attendee, adminInfo, transactionHistory)
if err != nil {
return transactionHistory, adminInfo, err
}
Expand All @@ -54,7 +56,7 @@ func (s *AttendeeServiceImplData) UpdateDuesTransactions(ctx context.Context, at
return updatedTransactionHistory, adminInfo, nil
}

func (s *AttendeeServiceImplData) compensateAllDues(ctx context.Context, attendee *entity.Attendee, newStatus status.Status, transactionHistory []paymentservice.Transaction) (bool, error) {
func (s *AttendeeServiceImplData) compensateAllDues(ctx context.Context, attendee *entity.Attendee, adminInfo *entity.AdminInfo, newStatus status.Status, transactionHistory []paymentservice.Transaction) (bool, error) {
oldDuesByVAT := s.oldDuesByVAT(transactionHistory)
updated := false

Expand All @@ -63,7 +65,7 @@ func (s *AttendeeServiceImplData) compensateAllDues(ctx context.Context, attende
for vatStr, duesBalance := range oldDuesByVAT {
if duesBalance != 0 {
updated = true
compensatingTx := s.duesTransactionForAttendee(attendee, -duesBalance, vatStr, comment)
compensatingTx := s.duesTransactionForAttendee(attendee, adminInfo, -duesBalance, vatStr, comment)
err := paymentservice.Get().AddTransaction(ctx, compensatingTx)
if err != nil {
return updated, err
Expand All @@ -73,7 +75,7 @@ func (s *AttendeeServiceImplData) compensateAllDues(ctx context.Context, attende
return updated, nil
}

func (s *AttendeeServiceImplData) compensateUnpaidDuesOnCancel(ctx context.Context, attendee *entity.Attendee, transactionHistory []paymentservice.Transaction) (bool, error) {
func (s *AttendeeServiceImplData) compensateUnpaidDuesOnCancel(ctx context.Context, attendee *entity.Attendee, adminInfo *entity.AdminInfo, transactionHistory []paymentservice.Transaction) (bool, error) {
_, paid, _, _ := s.balances(transactionHistory)
paid += s.pseudoPaymentsFromNegativeDues(transactionHistory)
updated := false
Expand All @@ -90,15 +92,15 @@ func (s *AttendeeServiceImplData) compensateUnpaidDuesOnCancel(ctx context.Conte
paid -= tx.Amount.GrossCent
} else if paid > 0 {
// payments partially cover the dues transaction, book compensating tx for remainder
remainderCompensatingTx := s.duesTransactionForAttendee(attendee, -(tx.Amount.GrossCent - paid), vatStr, "void unpaid dues on cancel")
remainderCompensatingTx := s.duesTransactionForAttendee(attendee, adminInfo, -(tx.Amount.GrossCent - paid), vatStr, "void unpaid dues on cancel")
err := paymentservice.Get().AddTransaction(ctx, remainderCompensatingTx)
if err != nil {
return updated, err
}
paid = 0
} else {
// no payments left, compensate completely
compensatingTx := s.duesTransactionForAttendee(attendee, -tx.Amount.GrossCent, vatStr, "void unpaid dues on cancel")
compensatingTx := s.duesTransactionForAttendee(attendee, adminInfo, -tx.Amount.GrossCent, vatStr, "void unpaid dues on cancel")
err := paymentservice.Get().AddTransaction(ctx, compensatingTx)
if err != nil {
return updated, err
Expand Down Expand Up @@ -132,7 +134,7 @@ func (s *AttendeeServiceImplData) adjustDuesAccordingToSelectedPackages(ctx cont
currentBalance, _ := oldDuesByVAT[vatStr]
if currentBalance != desiredBalance {
updated = true
diffTx := s.duesTransactionForAttendee(attendee, desiredBalance-currentBalance, vatStr, comment)
diffTx := s.duesTransactionForAttendee(attendee, adminInfo, desiredBalance-currentBalance, vatStr, comment)
err := paymentservice.Get().AddTransaction(ctx, diffTx)
if err != nil {
return updated, err
Expand Down Expand Up @@ -325,7 +327,7 @@ func (s *AttendeeServiceImplData) pseudoPaymentsFromNegativeDues(transactionHist
return
}

func (s *AttendeeServiceImplData) duesTransactionForAttendee(attendee *entity.Attendee, amount int64, vatStr string, comment string) paymentservice.Transaction {
func (s *AttendeeServiceImplData) duesTransactionForAttendee(attendee *entity.Attendee, adminInfo *entity.AdminInfo, amount int64, vatStr string, comment string) paymentservice.Transaction {
vat, _ := strconv.ParseFloat(vatStr, 64)

return paymentservice.Transaction{
Expand All @@ -341,9 +343,45 @@ func (s *AttendeeServiceImplData) duesTransactionForAttendee(attendee *entity.At
Status: paymentservice.Valid,
EffectiveDate: s.duesEffectiveDate(),
DueDate: s.duesDueDate(),
Reason: s.duesReason(attendee, adminInfo),
}
}

type ManualDues struct {
Amount int64 `json:"amount"`
Description string `json:"description"`
}

type DuesReason struct {
Packages []attendee.PackageState `json:"packages_list"`
ManualDues map[string]ManualDues `json:"manual_dues"`
Error bool `json:"error,omitempty"`
}

func (s *AttendeeServiceImplData) duesReason(attendee *entity.Attendee, adminInfo *entity.AdminInfo) string {
manualDues := make(map[string]ManualDues)
if adminInfo.ManualDues != 0 {
manualDues["admin"] = ManualDues{
Amount: adminInfo.ManualDues,
Description: adminInfo.ManualDuesDescription,
}
}

reason := DuesReason{
Packages: sortedPackageListFromCommaSeparatedWithCounts(attendee.Packages),
ManualDues: manualDues,
}

reasonBytes, err := json.Marshal(reason)
if err != nil {
// not really a problem
aulogging.Logger.NoCtx().Info().WithErr(err).Printf("failed to encode transaction reason to json - leaving blank and proceeding: %s", err.Error())
reasonBytes = []byte(`{"error":true}`)
}

return string(reasonBytes)
}

func (s *AttendeeServiceImplData) duesEffectiveDate() string {
return s.Now().Format(config.IsoDateFormat)
}
Expand Down
4 changes: 4 additions & 0 deletions test/acceptance/status_acc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1904,8 +1904,11 @@ func tstCreateTransaction(attid uint, ty paymentservice.TransactionType, amount

func tstCreateMatcherTransaction(attid uint, ty paymentservice.TransactionType, amount int64, comment string) paymentservice.Transaction {
method := paymentservice.Internal
reason := ""
if ty == paymentservice.Payment {
method = paymentservice.Credit
} else {
reason = tstGuessDuesReason(amount, comment)
}
return paymentservice.Transaction{
TransactionIdentifier: "",
Expand All @@ -1922,6 +1925,7 @@ func tstCreateMatcherTransaction(attid uint, ty paymentservice.TransactionType,
DueDate: "2022-12-22",
StatusHistory: nil, // TODO
Comment: comment,
Reason: reason,
}
}

Expand Down
38 changes: 38 additions & 0 deletions test/acceptance/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,43 @@ func tstParseJson(body string, dto interface{}) {
}

func tstValidAttendeeDues(amount int64, comment string) paymentservice.Transaction {
reason := tstGuessDuesReason(amount, comment)
return tstValidAttendeeDuesWithReason(amount, comment, reason)
}

func tstGuessDuesReason(amount int64, comment string) string {
// try to guess package list and manual dues based on amount (since we keep re-using the same package combinations with unique prices)
// for the few test cases where this doesn't work, must specify the reason yourself
pkgList := ""
switch amount {
default:
pkgList = `{"name":"attendance","count":1},{"name":"room-none","count":1},{"name":"sponsor2","count":1},{"name":"stage","count":1}`
}

reason := ""
if comment == "dues adjustment due to change in status or selected packages" ||
comment == "void unpaid dues on cancel" ||
comment == "admin info change" ||
comment == "remove dues balance - status changed to deleted" ||
comment == "remove dues balance - status changed to new" ||
comment == "remove dues balance - status changed to waiting" {
// normal package change or cancellation/deletion
switch amount {
case 13500:
reason = fmt.Sprintf(`{"packages_list":[%s],"manual_dues":{"admin":{"amount":-12000,"description":"we owe you this from last year"}}}`, pkgList)
case 33500:
reason = fmt.Sprintf(`{"packages_list":[%s],"manual_dues":{"admin":{"amount":8000,"description":"you still need to pay for last year"}}}`, pkgList)
default:
reason = fmt.Sprintf(`{"packages_list":[%s],"manual_dues":{}}`, pkgList)
}
} else {
// assume manual admin dues with provided comment
reason = fmt.Sprintf(`{"packages_list":[%s],"manual_dues":{"admin":{"amount":%d,"description":"%s"}}}`, pkgList, amount, comment)
}
return reason
}

func tstValidAttendeeDuesWithReason(amount int64, comment string, reason string) paymentservice.Transaction {
return paymentservice.Transaction{
TransactionIdentifier: "",
DebitorID: 1,
Expand All @@ -293,6 +330,7 @@ func tstValidAttendeeDues(amount int64, comment string) paymentservice.Transacti
EffectiveDate: "2022-12-08",
DueDate: "2022-12-22",
StatusHistory: nil, // TODO
Reason: reason,
}
}

Expand Down

0 comments on commit 2692e64

Please sign in to comment.