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 }