feat: cash advance

This commit is contained in:
Efril
2026-08-13 14:38:28 +07:00
parent e6078e3c0b
commit 2c6864147b
36 changed files with 2355 additions and 186 deletions
@@ -0,0 +1,101 @@
package mappers
import (
"testing"
"apskel-pos-be/internal/constants"
"apskel-pos-be/internal/entities"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
)
func TestCashAdvanceSettlementStatusFollowsTheMoney(t *testing.T) {
tests := []struct {
name string
amount float64
settled float64
returned float64
expectedStatus string
expectedRemaining float64
}{
{
name: "nothing spent or returned is still open",
amount: 500000,
expectedStatus: constants.CashAdvanceSettlementOpen,
expectedRemaining: 500000,
},
{
name: "some spending leaves it partial",
amount: 500000,
settled: 200000,
expectedStatus: constants.CashAdvanceSettlementPartial,
expectedRemaining: 300000,
},
{
name: "cash handed back counts the same as spending",
amount: 500000,
settled: 300000,
returned: 100000,
expectedStatus: constants.CashAdvanceSettlementPartial,
expectedRemaining: 100000,
},
{
name: "spending plus cash back covering the advance settles it",
amount: 500000,
settled: 420000,
returned: 80000,
expectedStatus: constants.CashAdvanceSettlementSettled,
expectedRemaining: 0,
},
{
// The team paid the difference out of pocket, so the outlet owes them.
name: "overspending settles the cash advance and goes negative",
amount: 500000,
settled: 620000,
expectedStatus: constants.CashAdvanceSettlementSettled,
expectedRemaining: -120000,
},
{
// Two decimals of money should not leave a cash advance a fraction short.
name: "a rounding crumb short still settles",
amount: 100000,
settled: 99999.999,
expectedStatus: constants.CashAdvanceSettlementSettled,
expectedRemaining: 0.001,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
response := CashAdvanceEntityToResponse(&entities.CashAdvance{
ID: uuid.New(),
TeamScope: constants.PurchaseTeamScopeCentral,
Amount: tt.amount,
SettledAmount: tt.settled,
ReturnedAmount: tt.returned,
})
require.Equal(t, tt.expectedStatus, response.SettlementStatus)
require.InDelta(t, tt.expectedRemaining, response.RemainingAmount, 0.0001)
})
}
}
func TestCashAdvanceTeamAlwaysRendered(t *testing.T) {
categoryID := uuid.New()
central := CashAdvanceEntityToResponse(&entities.CashAdvance{TeamScope: constants.PurchaseTeamScopeCentral})
require.NotNil(t, central.Team)
require.Equal(t, constants.PurchaseTeamCentralName, central.Team.Name)
require.Nil(t, central.Team.CategoryID)
category := CashAdvanceEntityToResponse(&entities.CashAdvance{
TeamScope: constants.PurchaseTeamScopeCategory,
TeamCategoryID: &categoryID,
TeamCategory: &entities.Category{ID: categoryID, Name: "Dapur"},
})
require.NotNil(t, category.Team)
require.Equal(t, "Dapur", category.Team.Name)
require.Equal(t, &categoryID, category.Team.CategoryID)
}