Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
91.67% covered (success)
91.67%
33 / 36
25.00% covered (danger)
25.00%
1 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
InvestorAgreementRepository
91.67% covered (success)
91.67%
33 / 36
25.00% covered (danger)
25.00%
1 / 4
11.07
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 findByInvestorId
94.44% covered (success)
94.44%
17 / 18
0.00% covered (danger)
0.00%
0 / 1
3.00
 updateStatus
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
2.02
 findOwnedDocumentName
90.91% covered (success)
90.91%
10 / 11
0.00% covered (danger)
0.00%
0 / 1
5.02
1<?php
2
3declare(strict_types=1);
4
5namespace App\Domain\Document\Repository;
6
7use App\Support\Row;
8use PDO;
9use RuntimeException;
10
11/**
12 * Reads/writes the investor_documents table — the local mirror of the
13 * outbound PandaDoc agreements (MPA, loan docs, etc.) sent to an investor.
14 *
15 * The live status of record lives in PandaDoc; this table is the index of
16 * which documents an investor has and their last-known status, refreshed by
17 * {@see \App\Domain\Document\Service\InvestorAgreementService}.
18 */
19final readonly class InvestorAgreementRepository
20{
21    public function __construct(
22        private PDO $pdo,
23    ) {}
24
25    /**
26     * @param int $investorId
27     * @return list<array{pandadocId: string, documentName: string, status: string, createdAt: string, updatedAt: string}>
28     */
29    public function findByInvestorId(int $investorId): array
30    {
31        $stmt = $this->pdo->prepare(
32            'SELECT pandadoc_id AS "pandadocId", document_name AS "documentName",
33                    status, created_at AS "createdAt", updated_at AS "updatedAt"
34             FROM investor_documents
35             WHERE investor_id = :investorId
36             ORDER BY created_at DESC',
37        );
38
39        if ($stmt === false) {
40            throw new RuntimeException('Failed to prepare statement');
41        }
42
43        $stmt->execute(['investorId' => $investorId]);
44
45        $documents = [];
46        foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
47            $rowArray = Row::from($row);
48            $documents[] = [
49                'pandadocId' => Row::string($rowArray, 'pandadocId'),
50                'documentName' => Row::string($rowArray, 'documentName'),
51                'status' => Row::string($rowArray, 'status'),
52                'createdAt' => Row::string($rowArray, 'createdAt'),
53                'updatedAt' => Row::string($rowArray, 'updatedAt'),
54            ];
55        }
56
57        return $documents;
58    }
59
60    public function updateStatus(string $pandadocId, string $status): void
61    {
62        $stmt = $this->pdo->prepare(
63            'UPDATE investor_documents SET status = :status, updated_at = CURRENT_TIMESTAMP WHERE pandadoc_id = :pandadocId',
64        );
65
66        if ($stmt === false) {
67            throw new RuntimeException('Failed to prepare statement');
68        }
69
70        $stmt->execute(['status' => $status, 'pandadocId' => $pandadocId]);
71    }
72
73    /**
74     * Verify an investor owns a given PandaDoc document and return its stored
75     * name (used for download filenames). Null if the investor doesn't own it.
76     * @param int $investorId
77     * @param string $pandadocId
78     */
79    public function findOwnedDocumentName(int $investorId, string $pandadocId): ?string
80    {
81        $stmt = $this->pdo->prepare(
82            'SELECT document_name FROM investor_documents
83             WHERE investor_id = :investorId AND pandadoc_id = :pandadocId',
84        );
85
86        if ($stmt === false) {
87            throw new RuntimeException('Failed to prepare statement');
88        }
89
90        $stmt->execute(['investorId' => $investorId, 'pandadocId' => $pandadocId]);
91        $row = $stmt->fetch(PDO::FETCH_ASSOC);
92
93        if (!is_array($row) || !isset($row['document_name']) || !is_string($row['document_name'])) {
94            return null;
95        }
96
97        return $row['document_name'];
98    }
99}