AbstractAccessKey   A
last analyzed

Complexity

Total Complexity 9

Size/Duplication

Total Lines 186
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 57
dl 0
loc 186
ccs 51
cts 51
cp 1
rs 10
c 1
b 0
f 0
wmc 9

8 Methods

Rating   Name   Duplication   Size   Complexity  
A validateModel() 0 5 2
A generateKey() 0 23 1
A calculateDigitFrom() 0 9 1
A createFromString() 0 3 1
A calculateDigit() 0 3 1
A __construct() 0 6 1
A format() 0 3 1
A loadFromKey() 0 28 1
1
<?php
2
3
namespace Brazanation\Documents\Sped;
4
5
use Brazanation\Documents\AbstractDocument;
6
use Brazanation\Documents\Cnpj;
7
use Brazanation\Documents\DigitCalculator;
8
use Brazanation\Documents\Sped\Exception\DocumentModel;
9
10
/**
11
 * Class SpedAccessKey
12
 *
13
 * @package Brazanation\Documents
14
 *
15
 * @property int          $state
16
 * @property \DateTime    $generatedAt
17
 * @property Cnpj         $cnpj
18
 * @property Model        $model
19
 * @property int          $sequence
20
 * @property int          $invoiceNumber
21
 * @property EmissionType $emissionType
22
 * @property int          $controlNumber
23
 */
24
abstract class AbstractAccessKey extends AbstractDocument
25
{
26
    const NUMBER_OF_DIGITS = 1;
27
28
    const LABEL = 'SpedAccessKey';
29
30
    const LENGTH = 44;
31
32
    const MASK = '$1 ';
33
34
    const REGEX = '/([\d]{4})/';
35
36
    /**
37
     * @var int
38
     */
39
    protected $state;
40
41
    /**
42
     * @var \DateTime
43
     */
44
    protected $generatedAt;
45
46
    /**
47
     * @var Cnpj
48
     */
49
    protected $cnpj;
50
51
    /**
52
     * @var Model
53
     */
54
    protected $model;
55
56
    /**
57
     * @var string
58
     */
59
    protected $sequence;
60
61
    /**
62
     * @var string
63
     */
64
    protected $invoiceNumber;
65
66
    /**
67
     * @var EmissionType
68
     */
69
    protected $emissionType;
70
71
    /**
72
     * @var string
73
     */
74
    protected $controlNumber;
75
76
    /**
77
     * SpedAccessKey constructor.
78
     *
79
     * @param $accessKey
80
     */
81 130
    public function __construct(string $accessKey)
82
    {
83 130
        $accessKey = preg_replace('/\D/', '', $accessKey);
84 130
        parent::__construct($accessKey, static::LENGTH, static::NUMBER_OF_DIGITS, static::LABEL);
85 30
        $this->validateModel($accessKey);
86 29
        $this->loadFromKey($accessKey);
87
    }
88
89 62
    public static function createFromString(string $number)
90
    {
91 62
        return parent::tryCreateFromString(static::class, $number, self::LENGTH, self::NUMBER_OF_DIGITS, self::LABEL);
92
    }
93
94
    /**
95
     * @return Model
96
     */
97
    abstract protected function defaultModel() : Model;
98
99 30
    protected function validateModel(string $accessKey)
100
    {
101 30
        $model = new Model(substr($accessKey, 20, 2));
0 ignored issues
show
Bug introduced by
substr($accessKey, 20, 2) of type string is incompatible with the type integer expected by parameter $model of Brazanation\Documents\Sped\Model::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

101
        $model = new Model(/** @scrutinizer ignore-type */ substr($accessKey, 20, 2));
Loading history...
102 30
        if (!$this->defaultModel()->equalsTo($model)) {
103 1
            throw DocumentModel::invalid($accessKey, static::LABEL);
104
        }
105
    }
106
107 29
    private function loadFromKey(string $accessKey)
108
    {
109 29
        $startPosition = 0;
110 29
        $this->state = substr($accessKey, $startPosition, 2);
0 ignored issues
show
Documentation Bug introduced by
The property $state was declared of type integer, but substr($accessKey, $startPosition, 2) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
111
112 29
        $startPosition += 2;
113 29
        $this->generatedAt = \DateTime::createFromFormat('ymd H:i:s', substr($accessKey, $startPosition, 4) . '01 00:00:00');
114
115 29
        $startPosition += 4;
116 29
        $this->cnpj = new Cnpj(substr($accessKey, $startPosition, 14));
117
118 29
        $startPosition += 14;
119 29
        $this->model = new Model(substr($accessKey, $startPosition, 2));
0 ignored issues
show
Bug introduced by
substr($accessKey, $startPosition, 2) of type string is incompatible with the type integer expected by parameter $model of Brazanation\Documents\Sped\Model::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

119
        $this->model = new Model(/** @scrutinizer ignore-type */ substr($accessKey, $startPosition, 2));
Loading history...
120
121 29
        $startPosition += 2;
122 29
        $this->sequence = substr($accessKey, $startPosition, 3);
0 ignored issues
show
Documentation Bug introduced by
The property $sequence was declared of type integer, but substr($accessKey, $startPosition, 3) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
123
124 29
        $startPosition += 3;
125 29
        $this->invoiceNumber = substr($accessKey, $startPosition, 9);
0 ignored issues
show
Documentation Bug introduced by
The property $invoiceNumber was declared of type integer, but substr($accessKey, $startPosition, 9) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
126
127 29
        $startPosition += 9;
128 29
        $this->emissionType = new EmissionType(substr($accessKey, $startPosition, 1));
0 ignored issues
show
Bug introduced by
substr($accessKey, $startPosition, 1) of type string is incompatible with the type integer expected by parameter $type of Brazanation\Documents\Sp...sionType::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

128
        $this->emissionType = new EmissionType(/** @scrutinizer ignore-type */ substr($accessKey, $startPosition, 1));
Loading history...
129
130 29
        $startPosition += 1;
131 29
        $this->controlNumber = substr($accessKey, $startPosition, 8);
0 ignored issues
show
Documentation Bug introduced by
The property $controlNumber was declared of type integer, but substr($accessKey, $startPosition, 8) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
132
133 29
        $startPosition += 8;
134 29
        $this->digit = substr($accessKey, $startPosition, 1);
135
    }
136
137
    /**
138
     * Generates a valid Sped Access Key.
139
     *
140
     * @param int          $state         IBGE state code.
141
     * @param \DateTime    $generatedAt   Year and month when invoice was created.
142
     * @param Cnpj         $cnpj          Cnpj from issuer.
143
     * @param Model        $model         Document model.
144
     * @param int          $sequence      Invoice sequence.
145
     * @param int          $invoiceNumber Invoice number.
146
     * @param EmissionType $emissionType  Emission Type.
147
     * @param int          $controlNumber Control number.
148
     *
149
     * @return AbstractAccessKey
150
     */
151 5
    protected static function generateKey(
152
        int $state,
153
        \DateTime $generatedAt,
154
        Cnpj $cnpj,
155
        Model $model,
156
        int $sequence,
157
        int $invoiceNumber,
158
        EmissionType $emissionType,
159
        int $controlNumber
160
    ) : AbstractAccessKey {
161 5
        $yearMonth = $generatedAt->format('ym');
162 5
        $sequence = str_pad($sequence, 3, 0, STR_PAD_LEFT);
163 5
        $invoiceNumber = str_pad($invoiceNumber, 9, 0, STR_PAD_LEFT);
164 5
        $controlNumber = str_pad($controlNumber, 8, 0, STR_PAD_LEFT);
165
166 5
        $baseNumber = "{$state}{$yearMonth}{$cnpj}{$model}{$sequence}{$invoiceNumber}{$emissionType}{$controlNumber}";
167
168 5
        $digit = self::calculateDigitFrom($baseNumber);
169
170 5
        $instance = new static("{$baseNumber}{$digit}");
171 5
        $instance->generatedAt = $generatedAt;
172
173 5
        return $instance;
174
    }
175
176
    /**
177
     * {@inheritdoc}
178
     */
179 10
    public function format() : string
180
    {
181 10
        return trim(preg_replace(self::REGEX, self::MASK, "{$this}"));
182
    }
183
184
    /**
185
     * {@inheritdoc}
186
     */
187 120
    public function calculateDigit(string $baseNumber) : string
188
    {
189 120
        return self::calculateDigitFrom($baseNumber);
190
    }
191
192
    /**
193
     * Calculate check digit from base number.
194
     *
195
     * It is static because is used from generate static method.
196
     *
197
     * @param string $baseNumber Base numeric section to be calculate your digit.
198
     *
199
     * @return string
200
     */
201 120
    public static function calculateDigitFrom(string $baseNumber) : string
202
    {
203 120
        $calculator = new DigitCalculator($baseNumber);
204 120
        $calculator->useComplementaryInsteadOfModule();
205 120
        $calculator->withModule(DigitCalculator::MODULE_11);
206 120
        $calculator->replaceWhen('0', 10, 11);
207 120
        $digit = $calculator->calculate();
208
209 120
        return "{$digit}";
210
    }
211
}
212