137 lines
4.6 KiB
Go
137 lines
4.6 KiB
Go
package refund
|
|
|
|
import (
|
|
"time"
|
|
|
|
"cloud.o-forge.io/core/oc-lib/models/common/pricing"
|
|
"cloud.o-forge.io/core/oc-lib/models/utils"
|
|
"cloud.o-forge.io/core/oc-lib/tools"
|
|
)
|
|
|
|
type RefundStatus int
|
|
|
|
const (
|
|
REFUND_PENDING RefundStatus = iota
|
|
REFUND_APPROVED // approuvé, en attente de traitement
|
|
REFUND_REJECTED // rejeté
|
|
REFUND_PROCESSING // en cours de virement/blockchain
|
|
REFUND_COMPLETED // remboursé
|
|
REFUND_CANCELLED // annulé avant approbation
|
|
)
|
|
|
|
func (s RefundStatus) String() string {
|
|
return [...]string{"pending", "approved", "rejected", "processing", "completed", "cancelled"}[s]
|
|
}
|
|
|
|
func RefundStatusList() []RefundStatus {
|
|
return []RefundStatus{REFUND_PENDING, REFUND_APPROVED, REFUND_REJECTED, REFUND_PROCESSING, REFUND_COMPLETED, REFUND_CANCELLED}
|
|
}
|
|
|
|
// Refund représente une demande de remboursement sur un paiement validé.
|
|
type Refund struct {
|
|
utils.AbstractObject
|
|
PaymentID string `json:"payment_id" bson:"payment_id" validate:"required"`
|
|
BillID string `json:"bill_id,omitempty" bson:"bill_id,omitempty"`
|
|
InvoiceID string `json:"invoice_id,omitempty" bson:"invoice_id,omitempty"`
|
|
RefundType pricing.RefundType `json:"refund_type" bson:"refund_type"`
|
|
Amount float64 `json:"amount" bson:"amount" validate:"required"`
|
|
Currency string `json:"currency" bson:"currency" default:"EUR"`
|
|
Reason string `json:"reason,omitempty" bson:"reason,omitempty"`
|
|
Status RefundStatus `json:"status" bson:"status"`
|
|
RequestedAt time.Time `json:"requested_at" bson:"requested_at"`
|
|
ProcessedAt *time.Time `json:"processed_at,omitempty" bson:"processed_at,omitempty"`
|
|
ProcessedByID string `json:"processed_by_id,omitempty" bson:"processed_by_id,omitempty"`
|
|
TransactionID string `json:"transaction_id,omitempty" bson:"transaction_id,omitempty"`
|
|
Notes string `json:"notes,omitempty" bson:"notes,omitempty"`
|
|
// ratio appliqué sur le montant original (0-100). 0 = non renseigné (remboursement total).
|
|
RefundRatio float64 `json:"refund_ratio,omitempty" bson:"refund_ratio,omitempty"`
|
|
}
|
|
|
|
// NewRefund crée une demande de remboursement total.
|
|
func NewRefund(paymentID, billID string, amount float64, currency string, refundType pricing.RefundType, reason string) *Refund {
|
|
return &Refund{
|
|
PaymentID: paymentID,
|
|
BillID: billID,
|
|
Amount: amount,
|
|
Currency: currency,
|
|
RefundType: refundType,
|
|
Reason: reason,
|
|
Status: REFUND_PENDING,
|
|
RequestedAt: time.Now().UTC(),
|
|
}
|
|
}
|
|
|
|
// NewPartialRefund crée une demande de remboursement partiel selon un ratio pourcentage.
|
|
func NewPartialRefund(paymentID, billID string, originalAmount, ratioPercent float64, currency string, refundType pricing.RefundType, reason string) *Refund {
|
|
amount := originalAmount * ratioPercent / 100
|
|
r := NewRefund(paymentID, billID, amount, currency, refundType, reason)
|
|
r.RefundRatio = ratioPercent
|
|
return r
|
|
}
|
|
|
|
// Approve approuve la demande de remboursement.
|
|
func (r *Refund) Approve(processedByID string) {
|
|
r.Status = REFUND_APPROVED
|
|
r.ProcessedByID = processedByID
|
|
}
|
|
|
|
// Reject rejette la demande de remboursement.
|
|
func (r *Refund) Reject(processedByID, notes string) {
|
|
now := time.Now().UTC()
|
|
r.Status = REFUND_REJECTED
|
|
r.ProcessedByID = processedByID
|
|
r.ProcessedAt = &now
|
|
r.Notes = notes
|
|
}
|
|
|
|
// Process passe le remboursement en cours de traitement.
|
|
func (r *Refund) Process() bool {
|
|
if r.Status != REFUND_APPROVED {
|
|
return false
|
|
}
|
|
r.Status = REFUND_PROCESSING
|
|
return true
|
|
}
|
|
|
|
// Complete finalise le remboursement avec l'identifiant de transaction.
|
|
func (r *Refund) Complete(transactionID string) {
|
|
now := time.Now().UTC()
|
|
r.Status = REFUND_COMPLETED
|
|
r.TransactionID = transactionID
|
|
r.ProcessedAt = &now
|
|
}
|
|
|
|
// Cancel annule la demande si elle est encore en attente.
|
|
func (r *Refund) Cancel() bool {
|
|
if r.Status != REFUND_PENDING {
|
|
return false
|
|
}
|
|
r.Status = REFUND_CANCELLED
|
|
return true
|
|
}
|
|
|
|
func (r *Refund) GetAccessor(request *tools.APIRequest) utils.Accessor {
|
|
return NewAccessor(request)
|
|
}
|
|
|
|
func (r *Refund) StoreDraftDefault() {
|
|
r.IsDraft = true
|
|
}
|
|
|
|
func (r *Refund) CanUpdate(set utils.DBObject) (bool, utils.DBObject) {
|
|
incoming := set.(*Refund)
|
|
if !r.IsDraft && r.Status != incoming.Status {
|
|
return true, &Refund{
|
|
Status: incoming.Status,
|
|
TransactionID: incoming.TransactionID,
|
|
Notes: incoming.Notes,
|
|
ProcessedByID: incoming.ProcessedByID,
|
|
}
|
|
}
|
|
return r.IsDraft, set
|
|
}
|
|
|
|
func (r *Refund) CanDelete() bool {
|
|
return r.IsDraft || r.Status == REFUND_PENDING
|
|
}
|