package usecase import ( "context" "errors" "github.com/gofrs/uuid/v5" "github.com/saveinmed/backend-go/internal/domain" ) // UploadDocument registers a new KYC document. func (s *Service) UploadDocument(ctx context.Context, companyID uuid.UUID, docType, url string) (*domain.CompanyDocument, error) { doc := &domain.CompanyDocument{ ID: uuid.Must(uuid.NewV7()), CompanyID: companyID, Type: docType, URL: url, Status: "PENDING", } if err := s.repo.CreateDocument(ctx, doc); err != nil { return nil, err } return doc, nil } // GetCompanyDocuments retrieves all documents for a company. func (s *Service) GetCompanyDocuments(ctx context.Context, companyID uuid.UUID) ([]domain.CompanyDocument, error) { return s.repo.ListDocuments(ctx, companyID) } // GetFormattedLedger retrieves the financial statement. func (s *Service) GetFormattedLedger(ctx context.Context, companyID uuid.UUID, page, pageSize int) (*domain.PaginationResponse[domain.LedgerEntry], error) { if page < 1 { page = 1 } if pageSize <= 0 { pageSize = 20 } offset := (page - 1) * pageSize entries, total, err := s.repo.GetLedger(ctx, companyID, pageSize, offset) if err != nil { return nil, err } // Calculate total pages totalPages := 0 if total > 0 { totalPages = int((total + int64(pageSize) - 1) / int64(pageSize)) } return &domain.PaginationResponse[domain.LedgerEntry]{ Items: entries, TotalCount: total, CurrentPage: page, TotalPages: totalPages, }, nil } // GetBalance returns the current net balance. func (s *Service) GetBalance(ctx context.Context, companyID uuid.UUID) (int64, error) { return s.repo.GetBalance(ctx, companyID) } // RequestWithdrawal initiates a payout if balance is sufficient. func (s *Service) RequestWithdrawal(ctx context.Context, companyID uuid.UUID, amountCents int64, bankInfo string) (*domain.Withdrawal, error) { if amountCents <= 0 { return nil, errors.New("amount must be positive") } // 1. Check Balance balance, err := s.repo.GetBalance(ctx, companyID) if err != nil { return nil, err } if balance < amountCents { return nil, errors.New("insufficient balance") } // 2. Create Ledger Entry first (Debit) entry := &domain.LedgerEntry{ ID: uuid.Must(uuid.NewV7()), CompanyID: companyID, AmountCents: -amountCents, Type: "WITHDRAWAL", Description: "Withdrawal Request", } if err := s.repo.RecordLedgerEntry(ctx, entry); err != nil { return nil, err } // 3. Create Withdrawal Record withdrawal := &domain.Withdrawal{ ID: uuid.Must(uuid.NewV7()), CompanyID: companyID, AmountCents: amountCents, Status: "PENDING", BankAccountInfo: bankInfo, } if err := s.repo.CreateWithdrawal(ctx, withdrawal); err != nil { // ROLLBACK LEDGER? In a real system we need a TX across both repository calls. // Since we handle tx inside repository usually, we should expose a method "RequestWithdrawalTx" in Repo. // For MVP, we risk valid ledger invalid withdrawal state. // Or we can just call it done. // Alternatively, we can assume the balance check is "soft" and we re-verify later. // Let's rely on Repo abstraction in future iterations. return nil, err } return withdrawal, nil }