From 7d3ae80fe76a17723d9c8614edc329469559f625 Mon Sep 17 00:00:00 2001 From: Neur0toxine Date: Tue, 17 Aug 2021 18:33:15 +0300 Subject: [PATCH] Support for `/api/v5/loyalty/loyalties/{id}` and `/api/v5/loyalty/account/{id} closes #108 --- .../Generator/SerializerGenerator.php | 39 ++++++- .../Template/CustomSerialization.php | 12 +- .../SerializedEntityCustomer.php | 8 ++ .../SerializedRelationAbstractCustomer.php | 9 +- src/Model/Entity/Loyalty/LoyaltyAccount.php | 16 +++ .../Orders/SerializedRelationCustomer.php | 13 +-- .../Loyalty/LoyaltyAccountResponse.php | 30 +++++ .../Response/Loyalty/LoyaltyResponse.php | 38 ++++++ src/ResourceGroup/Loyalty.php | 110 ++++++++++++++++++ tests/src/ResourceGroup/LoyaltyTest.php | 79 +++++++++++++ tests/src/ResourceGroup/OrdersTest.php | 7 ++ 11 files changed, 348 insertions(+), 13 deletions(-) create mode 100644 src/Model/Response/Loyalty/LoyaltyAccountResponse.php create mode 100644 src/Model/Response/Loyalty/LoyaltyResponse.php diff --git a/src/Component/Serializer/Generator/SerializerGenerator.php b/src/Component/Serializer/Generator/SerializerGenerator.php index 0083a1d..d83b55e 100644 --- a/src/Component/Serializer/Generator/SerializerGenerator.php +++ b/src/Component/Serializer/Generator/SerializerGenerator.php @@ -33,6 +33,8 @@ use RetailCrm\Api\Model\Entity\Customers\CustomerTag; use RetailCrm\Api\Model\Entity\CustomersCorporate\CustomerCorporate; use RetailCrm\Api\Component\Serializer\Template\CustomSerialization; use RetailCrm\Api\Component\Serializer\Type\PropertyTypeMixed; +use RetailCrm\Api\Model\Entity\CustomersCorporate\SerializedRelationAbstractCustomer; +use RetailCrm\Api\Model\Entity\Orders\SerializedRelationCustomer; use RuntimeException; use Symfony\Component\Filesystem\Filesystem; @@ -267,6 +269,20 @@ class SerializerGenerator new PreferredReducer(), new TakeBestReducer(), ]); + $serializedRelationAbstract = $this->metadataBuilder->build( + SerializedRelationAbstractCustomer::class, + [ + new PreferredReducer(), + new TakeBestReducer(), + ] + ); + $serializedRelation = $this->metadataBuilder->build( + SerializedRelationCustomer::class, + [ + new PreferredReducer(), + new TakeBestReducer(), + ] + ); $customerCode = $this->generateCodeForClass( $customerMetadata, $apiVersion, @@ -281,8 +297,29 @@ class SerializerGenerator $arrayPath, $modelPath ); + $serializedRelationAbstractCode = $this->generateCodeForClass( + $serializedRelationAbstract, + $apiVersion, + $serializerGroups, + $arrayPath, + $modelPath + ); + $serializedRelationCode = $this->generateCodeForClass( + $serializedRelation, + $apiVersion, + $serializerGroups, + $arrayPath, + $modelPath + ); - return $this->customTemplating->renderCustomerInterface($arrayPath, $modelPath, $customerCode, $corporateCode); + return $this->customTemplating->renderCustomerInterface( + $arrayPath, + $modelPath, + $customerCode, + $corporateCode, + $serializedRelationAbstractCode, + $serializedRelationCode + ); } /** diff --git a/src/Component/Serializer/Template/CustomSerialization.php b/src/Component/Serializer/Template/CustomSerialization.php index 3527c1e..0f5a7f7 100644 --- a/src/Component/Serializer/Template/CustomSerialization.php +++ b/src/Component/Serializer/Template/CustomSerialization.php @@ -28,6 +28,10 @@ if ({{modelPath}} instanceof \RetailCrm\Api\Model\Entity\Customers\Customer) { {{customerCode}} } elseif ({{modelPath}} instanceof \RetailCrm\Api\Model\Entity\CustomersCorporate\CustomerCorporate) { {{corporateCode}} +} elseif ({{modelPath}} instanceof \RetailCrm\Api\Model\Entity\CustomersCorporate\SerializedRelationAbstractCustomer) { + {{serializedRelationAbstractCode}} +} elseif ({{modelPath}} instanceof \RetailCrm\Api\Model\Entity\Orders\SerializedRelationCustomer) { + {{serializedRelationCode}} } if (0 === \count($jsonData{{jsonPath}})) { @@ -40,6 +44,8 @@ EOT; * @param string $modelPath * @param string $customerCode * @param string $corporateCode + * @param string $relationAbstractCode + * @param string $relationCode * * @return string * @throws \Twig\Error\LoaderError @@ -49,13 +55,17 @@ EOT; string $jsonPath, string $modelPath, string $customerCode, - string $corporateCode + string $corporateCode, + string $relationAbstractCode, + string $relationCode ): string { return $this->render(self::TMPL_CUSTOMER_INTERFACE, [ 'jsonPath' => $jsonPath, 'modelPath' => $modelPath, 'customerCode' => $customerCode, 'corporateCode' => $corporateCode, + 'serializedRelationAbstractCode' => $relationAbstractCode, + 'serializedRelationCode' => $relationCode, ]); } } diff --git a/src/Model/Entity/CustomersCorporate/SerializedEntityCustomer.php b/src/Model/Entity/CustomersCorporate/SerializedEntityCustomer.php index 6d17c51..b7b6f38 100644 --- a/src/Model/Entity/CustomersCorporate/SerializedEntityCustomer.php +++ b/src/Model/Entity/CustomersCorporate/SerializedEntityCustomer.php @@ -74,4 +74,12 @@ class SerializedEntityCustomer * @JMS\SerializedName("patronymic") */ public $patronymic; + + /** + * @var array + * + * @JMS\Type("array") + * @JMS\SerializedName("customFields") + */ + public $customFields; } diff --git a/src/Model/Entity/CustomersCorporate/SerializedRelationAbstractCustomer.php b/src/Model/Entity/CustomersCorporate/SerializedRelationAbstractCustomer.php index 18405f2..a14aaaa 100644 --- a/src/Model/Entity/CustomersCorporate/SerializedRelationAbstractCustomer.php +++ b/src/Model/Entity/CustomersCorporate/SerializedRelationAbstractCustomer.php @@ -10,6 +10,7 @@ namespace RetailCrm\Api\Model\Entity\CustomersCorporate; use RetailCrm\Api\Component\Serializer\Annotation as JMS; +use RetailCrm\Api\Interfaces\Orders\CustomerInterface; /** * Class SerializedRelationAbstractCustomer @@ -17,7 +18,7 @@ use RetailCrm\Api\Component\Serializer\Annotation as JMS; * @category SerializedRelationAbstractCustomer * @package RetailCrm\Api\Model\Entity\CustomersCorporate */ -class SerializedRelationAbstractCustomer +class SerializedRelationAbstractCustomer implements CustomerInterface { /** * @var int @@ -68,7 +69,7 @@ class SerializedRelationAbstractCustomer */ public static function withId(int $id): self { - $customer = new self(); + $customer = new self(); $customer->id = $id; return $customer; @@ -84,9 +85,9 @@ class SerializedRelationAbstractCustomer */ public static function withExternalId(string $externalId, string $site): self { - $customer = new self(); + $customer = new self(); $customer->externalId = $externalId; - $customer->site = $site; + $customer->site = $site; return $customer; } diff --git a/src/Model/Entity/Loyalty/LoyaltyAccount.php b/src/Model/Entity/Loyalty/LoyaltyAccount.php index c70c429..b083e50 100644 --- a/src/Model/Entity/Loyalty/LoyaltyAccount.php +++ b/src/Model/Entity/Loyalty/LoyaltyAccount.php @@ -131,4 +131,20 @@ class LoyaltyAccount * @JMS\SerializedName("level") */ public $level; + + /** + * @var string + * + * @JMS\Type("string") + * @JMS\SerializedName("status") + */ + public $status; + + /** + * @var \RetailCrm\Api\Model\Entity\Loyalty\Loyalty + * + * @JMS\Type("RetailCrm\Api\Model\Entity\Loyalty\Loyalty") + * @JMS\SerializedName("loyalty") + */ + public $loyalty; } diff --git a/src/Model/Entity/Orders/SerializedRelationCustomer.php b/src/Model/Entity/Orders/SerializedRelationCustomer.php index 2c1a1d7..1833b06 100644 --- a/src/Model/Entity/Orders/SerializedRelationCustomer.php +++ b/src/Model/Entity/Orders/SerializedRelationCustomer.php @@ -10,7 +10,6 @@ namespace RetailCrm\Api\Model\Entity\Orders; use RetailCrm\Api\Component\Serializer\Annotation as JMS; -use RetailCrm\Api\Interfaces\Orders\CustomerInterface; use RetailCrm\Api\Model\Entity\CustomersCorporate\SerializedRelationAbstractCustomer; /** @@ -19,7 +18,7 @@ use RetailCrm\Api\Model\Entity\CustomersCorporate\SerializedRelationAbstractCust * @category SerializedRelationCustomer * @package RetailCrm\Api\Model\Entity\Orders */ -class SerializedRelationCustomer extends SerializedRelationAbstractCustomer implements CustomerInterface +class SerializedRelationCustomer extends SerializedRelationAbstractCustomer { /** * @var string @@ -47,8 +46,8 @@ class SerializedRelationCustomer extends SerializedRelationAbstractCustomer impl */ public static function withIdAndType(int $id, string $type): self { - $customer = new self(); - $customer->id = $id; + $customer = new self(); + $customer->id = $id; $customer->type = $type; return $customer; @@ -65,10 +64,10 @@ class SerializedRelationCustomer extends SerializedRelationAbstractCustomer impl */ public static function withExternalIdAndType(string $externalId, string $type, string $site): self { - $customer = new self(); + $customer = new self(); $customer->externalId = $externalId; - $customer->type = $type; - $customer->site = $site; + $customer->type = $type; + $customer->site = $site; return $customer; } diff --git a/src/Model/Response/Loyalty/LoyaltyAccountResponse.php b/src/Model/Response/Loyalty/LoyaltyAccountResponse.php new file mode 100644 index 0000000..06e0a26 --- /dev/null +++ b/src/Model/Response/Loyalty/LoyaltyAccountResponse.php @@ -0,0 +1,30 @@ + + * + * @JMS\Type("array") + * @JMS\SerializedName("requiredFields") + */ + public $requiredFields; +} diff --git a/src/ResourceGroup/Loyalty.php b/src/ResourceGroup/Loyalty.php index b5e5aa5..3eebb1a 100644 --- a/src/ResourceGroup/Loyalty.php +++ b/src/ResourceGroup/Loyalty.php @@ -20,10 +20,12 @@ use RetailCrm\Api\Model\Request\Loyalty\LoyaltyCalculateRequest; use RetailCrm\Api\Model\Response\Loyalty\LoyaltiesResponse; use RetailCrm\Api\Model\Response\Loyalty\LoyaltyAccountActivateResponse; use RetailCrm\Api\Model\Response\Loyalty\LoyaltyAccountCreateResponse; +use RetailCrm\Api\Model\Response\Loyalty\LoyaltyAccountResponse; use RetailCrm\Api\Model\Response\Loyalty\LoyaltyAccountsResponse; use RetailCrm\Api\Model\Response\Loyalty\LoyaltyBonusCreditResponse; use RetailCrm\Api\Model\Response\Loyalty\LoyaltyBonusOperationsResponse; use RetailCrm\Api\Model\Response\Loyalty\LoyaltyCalculateResponse; +use RetailCrm\Api\Model\Response\Loyalty\LoyaltyResponse; /** * Class Loyalty @@ -406,6 +408,60 @@ class Loyalty extends AbstractApiResourceGroup return $response; } + /** + * Makes GET "/api/v5/loyalty/account/{id}" request. + * + * Example: + * ```php + * use RetailCrm\Api\Factory\SimpleClientFactory; + * use RetailCrm\Api\Interfaces\ApiExceptionInterface; + * + * $client = SimpleClientFactory::createClient('https://test.retailcrm.pro', 'apiKey'); + * + * try { + * $response = $client->loyalty->accountGet(1); + * } catch (ApiExceptionInterface $exception) { + * echo sprintf( + * 'Error from RetailCRM API (status code: %d): %s', + * $exception->getStatusCode(), + * $exception->getMessage() + * ); + * + * if (count($exception->getErrorResponse()->errors) > 0) { + * echo PHP_EOL . 'Errors: ' . implode(', ', $exception->getErrorResponse()->errors); + * } + * + * return; + * } + * + * echo 'Current loyalty account balance: ' . $response->loyaltyAccount->amount . ' bonuses.'; + * ``` + * + * @param int $id + * + * @return \RetailCrm\Api\Model\Response\Loyalty\LoyaltyAccountResponse + * @throws \RetailCrm\Api\Exception\Api\AccountDoesNotExistException + * @throws \RetailCrm\Api\Exception\Api\ApiErrorException + * @throws \RetailCrm\Api\Exception\Api\MissingCredentialsException + * @throws \RetailCrm\Api\Exception\Api\MissingParameterException + * @throws \RetailCrm\Api\Exception\Api\ValidationException + * @throws \RetailCrm\Api\Exception\Client\HandlerException + * @throws \RetailCrm\Api\Exception\Client\HttpClientException + * @throws \RetailCrm\Api\Interfaces\ApiExceptionInterface + * @throws \RetailCrm\Api\Interfaces\ClientExceptionInterface + */ + public function accountGet(int $id): LoyaltyAccountResponse + { + /** @var LoyaltyAccountResponse $response */ + $response = $this->sendRequest( + RequestMethod::GET, + 'loyalty/account/' . $id, + null, + LoyaltyAccountResponse::class + ); + return $response; + } + /** * Makes POST "/api/v5/loyalty/calculate" request. * @@ -546,4 +602,58 @@ class Loyalty extends AbstractApiResourceGroup ); return $response; } + + /** + * Makes GET "/api/v5/loyalty/loyalties/{id}" request. + * + * Example: + * ```php + * use RetailCrm\Api\Factory\SimpleClientFactory; + * use RetailCrm\Api\Interfaces\ApiExceptionInterface; + * + * $client = SimpleClientFactory::createClient('https://test.retailcrm.pro', 'apiKey'); + * + * try { + * $response = $client->loyalty->get(1); + * } catch (ApiExceptionInterface $exception) { + * echo sprintf( + * 'Error from RetailCRM API (status code: %d): %s', + * $exception->getStatusCode(), + * $exception->getMessage() + * ); + * + * if (count($exception->getErrorResponse()->errors) > 0) { + * echo PHP_EOL . 'Errors: ' . implode(', ', $exception->getErrorResponse()->errors); + * } + * + * return; + * } + * + * echo 'Loyalty program name: ' . $response->loyalty->name; + * ``` + * + * @param int $id + * + * @return \RetailCrm\Api\Model\Response\Loyalty\LoyaltyResponse + * @throws \RetailCrm\Api\Exception\Api\AccountDoesNotExistException + * @throws \RetailCrm\Api\Exception\Api\ApiErrorException + * @throws \RetailCrm\Api\Exception\Api\MissingCredentialsException + * @throws \RetailCrm\Api\Exception\Api\MissingParameterException + * @throws \RetailCrm\Api\Exception\Api\ValidationException + * @throws \RetailCrm\Api\Exception\Client\HandlerException + * @throws \RetailCrm\Api\Exception\Client\HttpClientException + * @throws \RetailCrm\Api\Interfaces\ApiExceptionInterface + * @throws \RetailCrm\Api\Interfaces\ClientExceptionInterface + */ + public function get(int $id): LoyaltyResponse + { + /** @var LoyaltyResponse $response */ + $response = $this->sendRequest( + RequestMethod::GET, + 'loyalty/loyalties/' . $id, + null, + LoyaltyResponse::class + ); + return $response; + } } diff --git a/tests/src/ResourceGroup/LoyaltyTest.php b/tests/src/ResourceGroup/LoyaltyTest.php index 3c558f3..1d37c23 100644 --- a/tests/src/ResourceGroup/LoyaltyTest.php +++ b/tests/src/ResourceGroup/LoyaltyTest.php @@ -595,6 +595,55 @@ EOF; self::assertModelEqualsToResponse($json, $response); } + public function testAccountGet(): void + { + $json = <<<'EOF' +{ + "success": true, + "loyaltyAccount": { + "active": false, + "id": 168, + "loyalty": { + "id": 1 + }, + "customer": { + "id": 5260, + "externalId": "5", + "site": "bitrix-test", + "customFields": { + "galkatrue": true + }, + "firstName": "admincd" + }, + "phoneNumber": "89226234577", + "amount": 0, + "ordersSum": 0, + "nextLevelSum": 10000, + "level": { + "type": "discount", + "id": 21, + "name": "Скидочный", + "privilegeSize": 20, + "privilegeSizePromo": 30 + }, + "createdAt": "2021-06-21 14:35:55", + "status": "not_confirmed", + "customFields": [] + } +} +EOF; + + $mock = static::createApiMockBuilder('loyalty/account/168'); + $mock->matchMethod(RequestMethod::GET) + ->reply(200) + ->withBody($json); + + $client = TestClientFactory::createClient($mock->getClient()); + $response = $client->loyalty->accountGet(168); + + self::assertModelEqualsToResponse($json, $response); + } + public function testCalculate(): void { $json = <<<'EOF' @@ -731,4 +780,34 @@ EOF; self::assertModelEqualsToResponse($json, $response); } + + public function testLoyaltiesGet(): void + { + $json = <<<'EOF' +{ + "success": true, + "loyalty": { + "active": true, + "blocked": false, + "id": 4, + "name": "Битрикс новый", + "confirmSmsCharge": false, + "confirmSmsRegistration": false, + "createdAt": "2021-03-17 18:08:02", + "activatedAt": "2021-03-17 18:09:43" + }, + "requiredFields": [] +} +EOF; + + $mock = static::createApiMockBuilder('loyalty/loyalties/4'); + $mock->matchMethod(RequestMethod::GET) + ->reply(200) + ->withBody($json); + + $client = TestClientFactory::createClient($mock->getClient()); + $response = $client->loyalty->get(4); + + self::assertModelEqualsToResponse($json, $response); + } } diff --git a/tests/src/ResourceGroup/OrdersTest.php b/tests/src/ResourceGroup/OrdersTest.php index d0018e5..ee6015f 100644 --- a/tests/src/ResourceGroup/OrdersTest.php +++ b/tests/src/ResourceGroup/OrdersTest.php @@ -11,6 +11,7 @@ namespace RetailCrm\Tests\ResourceGroup; use DateInterval; use DateTime; +use Psr\Http\Message\RequestInterface; use RetailCrm\Api\Enum\ByIdentifier; use RetailCrm\Api\Enum\CombineTechnique; use RetailCrm\Api\Enum\CountryCodeIso3166; @@ -958,6 +959,12 @@ EOF; $mock = static::createApiMockBuilder('orders/create'); $mock->matchMethod(RequestMethod::POST) ->matchBody(static::encodeForm($request)) + ->matchCallback(static function (RequestInterface $request) { + $data = []; + parse_str((string) $request->getBody(), $data); + + return false !== strpos($data['order'] ?? '', '"customer":{"id":4924}'); + }) ->reply(200) ->withBody($json);