Completed
Pull Request — master (#28)
by Antonio Oertel
02:19
created

AbstractAccessKey::calculateDigit()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 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 LABEL = 'SpedAccessKey';
27
28
    const LENGTH = 44;
29
30
    const REGEX = '/([\d]{4})/';
31
32
    const MASK = '$1 ';
33
34
    /**
35
     * @var int
36
     */
37
    protected $state;
38
39
    /**
40
     * @var \DateTime
41
     */
42
    protected $generatedAt;
43
44
    /**
45
     * @var Cnpj
46
     */
47
    protected $cnpj;
48
49
    /**
50
     * @var Model
51
     */
52
    protected $model;
53
54
    /**
55
     * @var string
56
     */
57
    protected $sequence;
58
59
    /**
60
     * @var string
61
     */
62
    protected $invoiceNumber;
63
64
    /**
65
     * @var EmissionType
66
     */
67
    protected $emissionType;
68
69
    /**
70
     * @var string
71
     */
72
    protected $controlNumber;
73
74
    /**
75
     * SpedAccessKey constructor.
76
     *
77
     * @param $accessKey
78
     */
79 83
    public function __construct($accessKey)
80
    {
81 83
        $accessKey = preg_replace('/\D/', '', $accessKey);
82 83
        parent::__construct($accessKey, static::LENGTH, 1, static::LABEL);
83 18
        $this->validateModel($accessKey);
84 17
        $this->loadFromKey($accessKey);
85 17
    }
86
87
    /**
88
     * @return Model
89
     */
90
    abstract protected function defaultModel();
91
92 18
    protected function validateModel($accessKey)
93
    {
94 18
        $model = new Model(substr($accessKey, 20, 2));
95 18
        if (!$this->defaultModel()->equalsTo($model)) {
96 1
            throw DocumentModel::invalid($accessKey, static::LABEL);
97
        }
98 17
    }
99
100 17
    private function loadFromKey($accessKey)
101
    {
102 17
        $startPosition = 0;
103 17
        $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...
104
105 17
        $startPosition += 2;
106 17
        $this->generatedAt = \DateTime::createFromFormat('ymd H:i:s', substr($accessKey, $startPosition, 4) . '01 00:00:00');
0 ignored issues
show
Documentation Bug introduced by
It seems like \DateTime::createFromFor...on, 4) . '01 00:00:00') can also be of type false. However, the property $generatedAt is declared as type object<DateTime>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

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