Files
apskel-pos-backend/internal/models/payment_method.go
efrilmandClaude Opus 5.5 cf5332c281 feat(loyalty): EnakPoint payment method
Adds the system payment method for paying with EnakPoint
(docs/prd-point-coin.md F9, §8, §10.5, PC-303).

Migration 000094 allows the point type, keeps one per organization with a
partial unique index, creates it for every existing organization, and adds
a trigger that creates it for new ones, as the walk-in customer is. It adds
payments.points_used and point_value. Their CHECK is written so it can
never be NULL: the PRD form, (both NULL) OR (both > 0), is NULL for
points_used with a NULL point_value, which a CHECK lets through, so a
payment could have lost the value a refund depends on. A test caught it.

The API cannot create, delete or retype the EnakPoint method, nor turn
another method into one; that answers 400. Renaming it is allowed. The
method list takes the outlet from ?outlet_id= or the user's outlet and
leaves EnakPoint out when that outlet does not accept it, filtered in the
query so the count stays right. The organization-wide active list is
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 11:33:25 +07:00

81 lines
2.4 KiB
Go

package models
import (
"apskel-pos-be/internal/constants"
"time"
"github.com/google/uuid"
)
type PaymentMethod struct {
ID uuid.UUID
OrganizationID uuid.UUID
Name string
Type constants.PaymentMethodType
Processor *string
Configuration map[string]interface{}
IsActive bool
CreatedAt time.Time
UpdatedAt time.Time
}
type CreatePaymentMethodRequest struct {
OrganizationID uuid.UUID `validate:"required"`
OutletID uuid.UUID `validate:"required"`
Name string `validate:"required,min=1,max=100"`
Type constants.PaymentMethodType `validate:"required"`
Processor *string `validate:"omitempty,max=100"`
Configuration map[string]interface{} `validate:"omitempty"`
IsActive *bool `validate:"omitempty"`
}
type UpdatePaymentMethodRequest struct {
Name *string `validate:"omitempty,min=1,max=100"`
Type *constants.PaymentMethodType `validate:"omitempty"`
Processor *string `validate:"omitempty,max=100"`
Configuration map[string]interface{} `validate:"omitempty"`
IsActive *bool `validate:"omitempty"`
}
type PaymentMethodResponse struct {
ID uuid.UUID
OrganizationID uuid.UUID
Name string
Type constants.PaymentMethodType
Processor *string
Configuration map[string]interface{}
IsActive bool
CreatedAt time.Time
UpdatedAt time.Time
}
type ListPaymentMethodsRequest struct {
OrganizationID *uuid.UUID
// When set, EnakPoint is left out unless the outlet accepts it (F9).
OutletID *uuid.UUID
Type *constants.PaymentMethodType
IsActive *bool
Search string
Page int `validate:"min=1"`
Limit int `validate:"min=1,max=100"`
}
type ListPaymentMethodsResponse struct {
PaymentMethods []PaymentMethodResponse
TotalCount int
Page int
Limit int
TotalPages int
}
func (pm *PaymentMethod) IsDigital() bool {
return pm.Type == constants.PaymentMethodTypeCard ||
pm.Type == constants.PaymentMethodTypeDigitalWallet ||
pm.Type == constants.PaymentMethodTypeEDC ||
pm.Type == constants.PaymentMethodTypeQR
}
func (pm *PaymentMethod) RequiresProcessor() bool {
return pm.IsDigital() && pm.Processor != nil
}