GET /customer/wallet now reads the EnakPoint & EnakCoin wallet (docs/prd-point-coin.md F6, PC-106): spendable point and coin balances, the rupiah value of one EnakPoint and of the balance, the nearest day each currency loses balance (grouped by Asia/Jakarta day), and recent ledger rows. The fields of the pre-wallet response stay, filled from the wallet, so app versions that read them keep working. Adds GET /customer/wallet/transactions with pagination and filters for currency, one or more types, and an inclusive date range. Each row shows where the value came from (additions) or went to (deductions) as in §8.1, and additions list their lots and earliest expiry. The counterparty id, the admin and the metadata are left out; the description already carries the masked name. A malformed query answers 400, a missing customer 404. /customer/points and /customer/tokens keep their shape and now read the wallet too, so customer_points_repository is no longer used for balances. Balances are what the customer can spend: lots that have expired but that the expiry job has not processed are not counted. The point value is read from organization_settings (loyalty.point.value, default 1) through a small repository that the typed settings reader in PC-109 will build on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
132 lines
4.7 KiB
Go
132 lines
4.7 KiB
Go
package models
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// Existing gamification models
|
|
type CreateCustomerPointsRequest struct {
|
|
CustomerID uuid.UUID `json:"customer_id" validate:"required"`
|
|
Balance int64 `json:"balance" validate:"min=0"`
|
|
}
|
|
|
|
type UpdateCustomerPointsRequest struct {
|
|
Balance int64 `json:"balance" validate:"min=0"`
|
|
}
|
|
|
|
type AddCustomerPointsRequest struct {
|
|
Balance int64 `json:"balance" validate:"required,min=1"`
|
|
}
|
|
|
|
type DeductCustomerPointsRequest struct {
|
|
Balance int64 `json:"balance" validate:"required,min=1"`
|
|
}
|
|
|
|
type CustomerPointsResponse struct {
|
|
ID uuid.UUID `json:"id"`
|
|
CustomerID uuid.UUID `json:"customer_id"`
|
|
Balance int64 `json:"balance"`
|
|
Customer *CustomerResponse `json:"customer,omitempty"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
}
|
|
|
|
type ListCustomerPointsQuery struct {
|
|
Page int `json:"page" validate:"min=1"`
|
|
Limit int `json:"limit" validate:"min=1,max=100"`
|
|
Search string `json:"search"`
|
|
SortBy string `json:"sort_by" validate:"omitempty,oneof=balance created_at updated_at"`
|
|
SortOrder string `json:"sort_order" validate:"omitempty,oneof=asc desc"`
|
|
}
|
|
|
|
type PaginatedCustomerPointsResponse struct {
|
|
Data []CustomerPointsResponse `json:"data"`
|
|
TotalCount int `json:"total_count"`
|
|
Page int `json:"page"`
|
|
Limit int `json:"limit"`
|
|
TotalPages int `json:"total_pages"`
|
|
}
|
|
|
|
// New customer API models
|
|
type GetCustomerPointsRequest struct {
|
|
// No additional fields needed - customer ID comes from JWT token
|
|
}
|
|
|
|
type GetCustomerTokensRequest struct {
|
|
// No additional fields needed - customer ID comes from JWT token
|
|
}
|
|
|
|
type GetCustomerWalletRequest struct {
|
|
// No additional fields needed - customer ID comes from JWT token
|
|
}
|
|
|
|
// Response Models
|
|
type GetCustomerPointsResponse struct {
|
|
Status string `json:"status"`
|
|
Message string `json:"message"`
|
|
Data *GetCustomerPointsResponseData `json:"data,omitempty"`
|
|
}
|
|
|
|
type GetCustomerPointsResponseData struct {
|
|
TotalPoints int64 `json:"total_points"`
|
|
PointsHistory []PointsHistoryItem `json:"points_history,omitempty"`
|
|
LastUpdated time.Time `json:"last_updated"`
|
|
}
|
|
|
|
type PointsHistoryItem struct {
|
|
ID string `json:"id"`
|
|
Points int64 `json:"points"`
|
|
Type string `json:"type"` // EARNED, REDEEMED, EXPIRED
|
|
Description string `json:"description"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
}
|
|
|
|
type GetCustomerTokensResponse struct {
|
|
Status string `json:"status"`
|
|
Message string `json:"message"`
|
|
Data *GetCustomerTokensResponseData `json:"data,omitempty"`
|
|
}
|
|
|
|
type GetCustomerTokensResponseData struct {
|
|
TotalTokens int64 `json:"total_tokens"`
|
|
TokensHistory []TokensHistoryItem `json:"tokens_history,omitempty"`
|
|
LastUpdated time.Time `json:"last_updated"`
|
|
}
|
|
|
|
type TokensHistoryItem struct {
|
|
ID string `json:"id"`
|
|
Tokens int64 `json:"tokens"`
|
|
Type string `json:"type"` // EARNED, REDEEMED, EXPIRED
|
|
Description string `json:"description"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
}
|
|
|
|
type GetCustomerWalletResponse struct {
|
|
Status string `json:"status"`
|
|
Message string `json:"message"`
|
|
Data *GetCustomerWalletResponseData `json:"data,omitempty"`
|
|
}
|
|
|
|
// GetCustomerWalletResponseData is GET /customer/wallet (docs/prd-point-coin.md F6).
|
|
type GetCustomerWalletResponseData struct {
|
|
// Balances the customer can spend now.
|
|
PointBalance int64 `json:"point_balance"`
|
|
CoinBalance int64 `json:"coin_balance"`
|
|
// Rupiah value of one EnakPoint, and of the whole point balance. EnakPoint only
|
|
// ever pays for orders, so the app shows this as "setara potongan Rp …" (K7).
|
|
PointValue int64 `json:"point_value"`
|
|
PointDiscountValue int64 `json:"point_discount_value"`
|
|
NearestExpiring CustomerWalletNearestExpiring `json:"nearest_expiring"`
|
|
RecentTransactions []CustomerWalletTransaction `json:"recent_transactions"`
|
|
|
|
// Deprecated: the shape of this endpoint before the wallet, kept for app versions
|
|
// that still read it. Filled from the wallet like the fields above.
|
|
TotalPoints int64 `json:"total_points"`
|
|
TotalTokens int64 `json:"total_tokens"`
|
|
PointsHistory []PointsHistoryItem `json:"points_history,omitempty"`
|
|
TokensHistory []TokensHistoryItem `json:"tokens_history,omitempty"`
|
|
LastUpdated time.Time `json:"last_updated"`
|
|
}
|