Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
33 / 33
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
PandaDocFieldMapper
100.00% covered (success)
100.00%
33 / 33
100.00% covered (success)
100.00%
3 / 3
7
100.00% covered (success)
100.00%
1 / 1
 map
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
4
 mapForMpa
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
1
 mapForW9
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4
5namespace App\Domain\Document\Service;
6
7use App\Domain\Investor\Data\InvestorData;
8
9/**
10 * Maps an investor onto the field/token values for a specific PandaDoc template.
11 *
12 * One method per template. Templates are dispatched by ID so renames in
13 * PandaDoc don't break the mapping. Field names here MUST match the names
14 * configured in the PandaDoc template editor — verify with
15 * `pandadoc:inspect-template` after any template change.
16 *
17 * @phpstan-type Mapping array{fields: array<string, array{value: string}>, tokens: array<string, string>}
18 */
19final readonly class PandaDocFieldMapper
20{
21    public const string MPA_TEMPLATE_ID = 'mxMQujVjQbVTuS2wD47dEK';
22    public const string W9_TEMPLATE_ID = 'EhTXUKKhtSMXCf74rJkcGD';
23
24    /**
25     * @param InvestorData $investor
26     * @param string $templateId
27     * @return Mapping
28     */
29    public function map(InvestorData $investor, string $templateId): array
30    {
31        return match ($templateId) {
32            self::MPA_TEMPLATE_ID => $this->mapForMpa($investor),
33            self::W9_TEMPLATE_ID => $this->mapForW9($investor),
34            default => ['fields' => [], 'tokens' => []],
35        };
36    }
37
38    /**
39     * Master Program Agreement — individual signer (HNWI in personal capacity).
40     *
41     * @param InvestorData $investor
42     * @return Mapping
43     */
44    private function mapForMpa(InvestorData $investor): array
45    {
46        $today = date('Y-m-d');
47        $fullName = trim($investor->firstName . ' ' . $investor->lastName);
48
49        return [
50            'fields' => [
51                'Effective Date' => ['value' => $today],
52                'Client Name' => ['value' => $fullName],
53                'Client Title' => ['value' => 'Self'],
54                'Client Date' => ['value' => $today],
55            ],
56            'tokens' => [],
57        ];
58    }
59
60    /**
61     * IRS Form W-9 — individual.
62     *
63     * @param InvestorData $investor
64     * @return Mapping
65     */
66    private function mapForW9(InvestorData $investor): array
67    {
68        $line1 = $investor->addressLine1 ?? '';
69        $line2 = $investor->addressLine2 ?? '';
70        $street = $line2 !== '' ? $line1 . ', ' . $line2 : $line1;
71
72        $cityStateZip = sprintf(
73            '%s, %s %s',
74            $investor->city ?? '',
75            $investor->state ?? '',
76            $investor->zipCode ?? '',
77        );
78
79        return [
80            'fields' => [
81                'Name' => ['value' => trim($investor->firstName . ' ' . $investor->lastName)],
82                'Street, Apt, Suite' => ['value' => $street],
83                'City, State, Zip' => ['value' => $cityStateZip],
84            ],
85            'tokens' => [],
86        ];
87    }
88}