Skip to content

Commit ea22b61

Browse files
bessudnovJamoliddin Iloldinov
authored andcommitted
squashed commits
1 parent 0e2bc47 commit ea22b61

3 files changed

Lines changed: 232 additions & 12 deletions

File tree

src/AmoCRM/Client/AmoCRMApiRequest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class AmoCRMApiRequest
3636
public const CONNECT_TIMEOUT = 5;
3737
public const REQUEST_TIMEOUT = 20;
3838
//TODO Do not forget to change this on each release
39-
public const LIBRARY_VERSION = '1.9.1';
39+
public const LIBRARY_VERSION = '1.16.0';
4040
public const USER_AGENT = 'amoCRM-API-Library/' . self::LIBRARY_VERSION;
4141

4242
public const SUCCESS_STATUSES = [

src/AmoCRM/Models/LeadModel.php

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ class LeadModel extends BaseApiModel implements
111111
protected $closestTaskAt;
112112

113113
/**
114-
* @var int
114+
* @var int|float|null
115115
*/
116116
protected $price;
117117

@@ -252,21 +252,28 @@ public function setName(string $name): self
252252
return $this;
253253
}
254254

255+
public function getPrice(): ?int
256+
{
257+
return isset($this->price) ? (int)$this->price : null;
258+
}
259+
255260
/**
256-
* @return null|int
261+
* На данный момент поддержка минорных единиц валют доступна только в Kommo
257262
*/
258-
public function getPrice(): ?int
263+
public function getPriceWithMinorUnits(): ?float
259264
{
260-
return $this->price;
265+
return isset($this->price) ? (float)$this->price : null;
261266
}
262267

263268
/**
264-
* @param null|int $price
265-
*
266-
* @return self
269+
* @param int|float|null $price
267270
*/
268-
public function setPrice(?int $price): self
271+
public function setPrice($price): self
269272
{
273+
if (!is_int($price) && !is_float($price) && !is_null($price)) {
274+
throw new InvalidArgumentException('Lead price must be an integer, float or null.');
275+
}
276+
270277
$this->price = $price;
271278

272279
return $this;
@@ -744,7 +751,9 @@ public static function fromArray(array $lead): self
744751
$leadModel->setName($lead['name']);
745752
}
746753

747-
if (array_key_exists('price', $lead) && !is_null($lead['price'])) {
754+
if (array_key_exists('price_with_minor_units', $lead) && !is_null($lead['price_with_minor_units'])) {
755+
$leadModel->setPrice((float)$lead['price_with_minor_units']);
756+
} elseif (array_key_exists('price', $lead) && !is_null($lead['price'])) {
748757
$leadModel->setPrice((int)$lead['price']);
749758
}
750759

@@ -861,6 +870,7 @@ public function toArray(): array
861870
$result = [
862871
'name' => $this->getName(),
863872
'price' => $this->getPrice(),
873+
'price_with_minor_units' => $this->getPriceWithMinorUnits(),
864874
'responsible_user_id' => $this->getResponsibleUserId(),
865875
'group_id' => $this->getGroupId(),
866876
'status_id' => $this->getStatusId(),
@@ -932,8 +942,8 @@ public function toApi(?string $requestId = "0"): array
932942
$result['name'] = $this->getName();
933943
}
934944

935-
if (!is_null($this->getPrice())) {
936-
$result['price'] = $this->getPrice();
945+
if (!is_null($this->getPriceWithMinorUnits())) {
946+
$result['price'] = $this->getPriceWithMinorUnits();
937947
}
938948

939949
if (!is_null($this->getResponsibleUserId())) {
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cases\Price;
6+
7+
use AmoCRM\Exceptions\InvalidArgumentException;
8+
use AmoCRM\Models\LeadModel;
9+
use PHPUnit\Framework\TestCase;
10+
11+
class LeadPriceTest extends TestCase
12+
{
13+
public function testSetPriceWithIntegerPreservesLegacyBehaviour(): void
14+
{
15+
$lead = (new LeadModel())->setPrice(100);
16+
17+
$this->assertPriceState($lead, 100, 100.0);
18+
}
19+
20+
public function testSetPriceWithFloatExposesMinorUnitsAndTruncatesLegacyGetter(): void
21+
{
22+
$lead = (new LeadModel())->setPrice(100.5);
23+
24+
$this->assertPriceState($lead, 100, 100.5);
25+
}
26+
27+
public function testSetPriceWithSmallFractionKeepsLegacyPriceAtZero(): void
28+
{
29+
$lead = (new LeadModel())->setPrice(0.1);
30+
31+
$this->assertPriceState($lead, 0, 0.1);
32+
$this->assertSame(0, $lead->toArray()['price']);
33+
$this->assertSame(0.1, $lead->toArray()['price_with_minor_units']);
34+
$this->assertSame(0.1, $lead->toApi()['price']);
35+
}
36+
37+
public function testSetPriceWithLargeFractionDoesNotRoundLegacyPriceUp(): void
38+
{
39+
$lead = (new LeadModel())->setPrice(99999999.999);
40+
41+
$this->assertPriceState($lead, 99999999, 99999999.999);
42+
$this->assertNotSame(100000000, $lead->getPrice());
43+
$this->assertSame(99999999, $lead->toArray()['price']);
44+
$this->assertSame(99999999.999, $lead->toArray()['price_with_minor_units']);
45+
$this->assertSame(99999999.999, $lead->toApi()['price']);
46+
}
47+
48+
public function testSetPriceWithNullLeavesBothRepresentationsNull(): void
49+
{
50+
$lead = (new LeadModel())->setPrice(null);
51+
52+
$this->assertPriceState($lead, null, null);
53+
}
54+
55+
/**
56+
* @throws InvalidArgumentException
57+
*/
58+
public function testFromArrayUsesLegacyPriceWhenOnlyPriceIsProvided(): void
59+
{
60+
$this->assertPriceState(
61+
LeadModel::fromArray(['id' => 1, 'price' => 200]),
62+
200,
63+
200.0
64+
);
65+
}
66+
67+
/**
68+
* @throws InvalidArgumentException
69+
*/
70+
public function testFromArrayUsesMinorUnitsPriceWhenProvided(): void
71+
{
72+
$this->assertPriceState(
73+
LeadModel::fromArray(['id' => 1, 'price_with_minor_units' => 200.75]),
74+
200,
75+
200.75
76+
);
77+
}
78+
79+
/**
80+
* @throws InvalidArgumentException
81+
*/
82+
public function testFromArrayWithSmallFractionKeepsLegacyPriceAtZero(): void
83+
{
84+
$this->assertPriceState(
85+
LeadModel::fromArray(['id' => 1, 'price_with_minor_units' => 0.1]),
86+
0,
87+
0.1
88+
);
89+
}
90+
91+
/**
92+
* @throws InvalidArgumentException
93+
*/
94+
public function testFromArrayWithLargeFractionDoesNotRoundLegacyPriceUp(): void
95+
{
96+
$lead = LeadModel::fromArray([
97+
'id' => 1,
98+
'price_with_minor_units' => 99999999.999,
99+
]);
100+
101+
$this->assertPriceState($lead, 99999999, 99999999.999);
102+
$this->assertNotSame(100000000, $lead->getPrice());
103+
}
104+
105+
/**
106+
* @throws InvalidArgumentException
107+
*/
108+
public function testFromArrayPrefersMinorUnitsOverLegacyPrice(): void
109+
{
110+
$lead = LeadModel::fromArray([
111+
'id' => 1,
112+
'price' => 300,
113+
'price_with_minor_units' => 300.99,
114+
]);
115+
116+
$this->assertPriceState($lead, 300, 300.99);
117+
}
118+
119+
/**
120+
* @throws InvalidArgumentException
121+
*/
122+
public function testFromArrayFallsBackToLegacyPriceWhenMinorUnitsIsNull(): void
123+
{
124+
$lead = LeadModel::fromArray([
125+
'id' => 1,
126+
'price' => 400,
127+
'price_with_minor_units' => null,
128+
]);
129+
130+
$this->assertPriceState($lead, 400, 400.0);
131+
}
132+
133+
public function testToArrayIncludesBothPriceRepresentations(): void
134+
{
135+
$this->assertSame(
136+
[
137+
'name' => null,
138+
'price' => 123,
139+
'price_with_minor_units' => 123.45,
140+
'responsible_user_id' => null,
141+
'group_id' => null,
142+
'status_id' => null,
143+
'pipeline_id' => null,
144+
'loss_reason_id' => null,
145+
'source_id' => null,
146+
'created_by' => null,
147+
'updated_by' => null,
148+
'created_at' => null,
149+
'updated_at' => null,
150+
'closed_at' => null,
151+
'closest_task_at' => null,
152+
'is_deleted' => null,
153+
'custom_fields_values' => null,
154+
'score' => null,
155+
'account_id' => null,
156+
'id' => 1,
157+
],
158+
(new LeadModel())
159+
->setId(1)
160+
->setPrice(123.45)
161+
->toArray()
162+
);
163+
}
164+
165+
public function testToApiUsesMinorUnitsInPriceFieldForFloatInput(): void
166+
{
167+
$this->assertSame(
168+
['price' => 100.5, 'request_id' => '0'],
169+
(new LeadModel())->setPrice(100.5)->toApi()
170+
);
171+
}
172+
173+
public function testToApiUsesNumericPriceFieldForIntegerInput(): void
174+
{
175+
$this->assertSame(
176+
['price' => 100.0, 'request_id' => '0'],
177+
(new LeadModel())->setPrice(100)->toApi()
178+
);
179+
}
180+
181+
public function testToApiOmitsPriceWhenItWasNotSet(): void
182+
{
183+
$this->assertSame(['request_id' => '0'], (new LeadModel())->toApi());
184+
}
185+
186+
public function testSetPriceWithStringThrowsException(): void
187+
{
188+
$this->expectException(InvalidArgumentException::class);
189+
$this->expectExceptionMessage('Lead price must be an integer, float or null.');
190+
191+
(new LeadModel())->setPrice('100.0');
192+
}
193+
194+
public function testSetPriceWithArrayThrowsException(): void
195+
{
196+
$this->expectException(InvalidArgumentException::class);
197+
$this->expectExceptionMessage('Lead price must be an integer, float or null.');
198+
199+
(new LeadModel())->setPrice([100]);
200+
}
201+
202+
private function assertPriceState(LeadModel $lead, ?int $legacyPrice, ?float $priceWithMinorUnits): void
203+
{
204+
$this->assertSame($legacyPrice, $lead->getPrice());
205+
206+
is_null($priceWithMinorUnits)
207+
? $this->assertNull($lead->getPriceWithMinorUnits())
208+
: $this->assertSame($priceWithMinorUnits, $lead->getPriceWithMinorUnits());
209+
}
210+
}

0 commit comments

Comments
 (0)