| @@ -1398,6 +1398,32 @@ paths: | |||
| style: form | |||
| explode: true | |||
| allowReserved: false | |||
| - | |||
| name: partner.type | |||
| in: query | |||
| description: '' | |||
| required: false | |||
| deprecated: false | |||
| allowEmptyValue: true | |||
| schema: | |||
| type: string | |||
| style: form | |||
| explode: false | |||
| allowReserved: false | |||
| - | |||
| name: 'partner.type[]' | |||
| in: query | |||
| description: '' | |||
| required: false | |||
| deprecated: false | |||
| allowEmptyValue: true | |||
| schema: | |||
| type: array | |||
| items: | |||
| type: string | |||
| style: form | |||
| explode: true | |||
| allowReserved: false | |||
| deprecated: false | |||
| post: | |||
| operationId: api_partner_follows_post | |||
| @@ -1645,6 +1671,32 @@ paths: | |||
| style: form | |||
| explode: false | |||
| allowReserved: false | |||
| - | |||
| name: partner.type | |||
| in: query | |||
| description: '' | |||
| required: false | |||
| deprecated: false | |||
| allowEmptyValue: true | |||
| schema: | |||
| type: string | |||
| style: form | |||
| explode: false | |||
| allowReserved: false | |||
| - | |||
| name: 'partner.type[]' | |||
| in: query | |||
| description: '' | |||
| required: false | |||
| deprecated: false | |||
| allowEmptyValue: true | |||
| schema: | |||
| type: array | |||
| items: | |||
| type: string | |||
| style: form | |||
| explode: true | |||
| allowReserved: false | |||
| deprecated: false | |||
| post: | |||
| operationId: api_partner_products_post | |||
| @@ -2692,6 +2744,46 @@ paths: | |||
| allowReserved: false | |||
| deprecated: false | |||
| parameters: [] | |||
| '/api/sale_summaries/{userId}': | |||
| get: | |||
| operationId: api_sale_summaries_userId_get | |||
| tags: | |||
| - SaleSummary | |||
| responses: | |||
| 200: | |||
| description: 'SaleSummary resource' | |||
| content: | |||
| application/ld+json: | |||
| schema: | |||
| $ref: '#/components/schemas/SaleSummary.jsonld' | |||
| application/json: | |||
| schema: | |||
| $ref: '#/components/schemas/SaleSummary' | |||
| text/html: | |||
| schema: | |||
| $ref: '#/components/schemas/SaleSummary' | |||
| application/hal+json: | |||
| schema: | |||
| $ref: '#/components/schemas/SaleSummary.jsonhal' | |||
| 404: | |||
| description: 'Resource not found' | |||
| summary: 'Retrieves a SaleSummary resource.' | |||
| description: 'Retrieves a SaleSummary resource.' | |||
| parameters: | |||
| - | |||
| name: userId | |||
| in: path | |||
| description: 'SaleSummary identifier' | |||
| required: true | |||
| deprecated: false | |||
| allowEmptyValue: false | |||
| schema: | |||
| type: string | |||
| style: simple | |||
| explode: false | |||
| allowReserved: false | |||
| deprecated: false | |||
| parameters: [] | |||
| /api/sales: | |||
| get: | |||
| operationId: api_sales_get_collection | |||
| @@ -3595,6 +3687,21 @@ paths: | |||
| style: form | |||
| explode: true | |||
| allowReserved: false | |||
| - | |||
| name: 'order[product.name]' | |||
| in: query | |||
| description: '' | |||
| required: false | |||
| deprecated: false | |||
| allowEmptyValue: true | |||
| schema: | |||
| type: string | |||
| enum: | |||
| - asc | |||
| - desc | |||
| style: form | |||
| explode: false | |||
| allowReserved: false | |||
| deprecated: false | |||
| post: | |||
| operationId: api_user_products_post | |||
| @@ -5162,11 +5269,8 @@ components: | |||
| - string | |||
| - 'null' | |||
| partner: | |||
| type: | |||
| - string | |||
| - 'null' | |||
| format: iri-reference | |||
| example: 'https://example.com/' | |||
| description: '?PartnerApi' | |||
| $ref: '#/components/schemas/Partner' | |||
| partnerName: | |||
| readOnly: true | |||
| type: | |||
| @@ -5214,11 +5318,8 @@ components: | |||
| - string | |||
| - 'null' | |||
| partner: | |||
| type: | |||
| - string | |||
| - 'null' | |||
| format: iri-reference | |||
| example: 'https://example.com/' | |||
| description: '?PartnerApi' | |||
| $ref: '#/components/schemas/Partner.jsonhal' | |||
| partnerName: | |||
| readOnly: true | |||
| type: | |||
| @@ -5280,11 +5381,8 @@ components: | |||
| - string | |||
| - 'null' | |||
| partner: | |||
| type: | |||
| - string | |||
| - 'null' | |||
| format: iri-reference | |||
| example: 'https://example.com/' | |||
| description: '?PartnerApi' | |||
| $ref: '#/components/schemas/Partner.jsonld' | |||
| partnerName: | |||
| readOnly: true | |||
| type: | |||
| @@ -5314,22 +5412,16 @@ components: | |||
| - product | |||
| properties: | |||
| partner: | |||
| type: | |||
| - string | |||
| - 'null' | |||
| format: iri-reference | |||
| example: 'https://example.com/' | |||
| description: '?PartnerApi' | |||
| $ref: '#/components/schemas/Partner' | |||
| partnerName: | |||
| readOnly: true | |||
| type: | |||
| - string | |||
| - 'null' | |||
| product: | |||
| type: | |||
| - string | |||
| - 'null' | |||
| format: iri-reference | |||
| example: 'https://example.com/' | |||
| description: '?ProductApi' | |||
| $ref: '#/components/schemas/Product' | |||
| productName: | |||
| readOnly: true | |||
| type: | |||
| @@ -5359,22 +5451,16 @@ components: | |||
| type: string | |||
| format: iri-reference | |||
| partner: | |||
| type: | |||
| - string | |||
| - 'null' | |||
| format: iri-reference | |||
| example: 'https://example.com/' | |||
| description: '?PartnerApi' | |||
| $ref: '#/components/schemas/Partner.jsonhal' | |||
| partnerName: | |||
| readOnly: true | |||
| type: | |||
| - string | |||
| - 'null' | |||
| product: | |||
| type: | |||
| - string | |||
| - 'null' | |||
| format: iri-reference | |||
| example: 'https://example.com/' | |||
| description: '?ProductApi' | |||
| $ref: '#/components/schemas/Product.jsonhal' | |||
| productName: | |||
| readOnly: true | |||
| type: | |||
| @@ -5418,22 +5504,16 @@ components: | |||
| readOnly: true | |||
| type: string | |||
| partner: | |||
| type: | |||
| - string | |||
| - 'null' | |||
| format: iri-reference | |||
| example: 'https://example.com/' | |||
| description: '?PartnerApi' | |||
| $ref: '#/components/schemas/Partner.jsonld' | |||
| partnerName: | |||
| readOnly: true | |||
| type: | |||
| - string | |||
| - 'null' | |||
| product: | |||
| type: | |||
| - string | |||
| - 'null' | |||
| format: iri-reference | |||
| example: 'https://example.com/' | |||
| description: '?ProductApi' | |||
| $ref: '#/components/schemas/Product.jsonld' | |||
| productName: | |||
| readOnly: true | |||
| type: | |||
| @@ -5484,6 +5564,16 @@ components: | |||
| type: | |||
| - string | |||
| - 'null' | |||
| partnerType: | |||
| readOnly: true | |||
| type: | |||
| - string | |||
| - 'null' | |||
| enum: | |||
| - customer | |||
| - supplier | |||
| - service | |||
| - null | |||
| product: | |||
| type: | |||
| - string | |||
| @@ -5623,6 +5713,16 @@ components: | |||
| type: | |||
| - string | |||
| - 'null' | |||
| partnerType: | |||
| readOnly: true | |||
| type: | |||
| - string | |||
| - 'null' | |||
| enum: | |||
| - customer | |||
| - supplier | |||
| - service | |||
| - null | |||
| product: | |||
| type: | |||
| - string | |||
| @@ -5769,6 +5869,16 @@ components: | |||
| type: | |||
| - string | |||
| - 'null' | |||
| partnerType: | |||
| readOnly: true | |||
| type: | |||
| - string | |||
| - 'null' | |||
| enum: | |||
| - customer | |||
| - supplier | |||
| - service | |||
| - null | |||
| product: | |||
| type: | |||
| - string | |||
| @@ -6276,6 +6386,8 @@ components: | |||
| description: '' | |||
| deprecated: false | |||
| properties: | |||
| userId: | |||
| type: integer | |||
| owner: | |||
| type: | |||
| - string | |||
| @@ -6308,6 +6420,8 @@ components: | |||
| href: | |||
| type: string | |||
| format: iri-reference | |||
| userId: | |||
| type: integer | |||
| owner: | |||
| type: | |||
| - string | |||
| @@ -6337,6 +6451,25 @@ components: | |||
| '@type': | |||
| readOnly: true | |||
| type: string | |||
| '@context': | |||
| readOnly: true | |||
| oneOf: | |||
| - | |||
| type: string | |||
| - | |||
| type: object | |||
| properties: | |||
| '@vocab': | |||
| type: string | |||
| hydra: | |||
| type: string | |||
| enum: ['http://www.w3.org/ns/hydra/core#'] | |||
| required: | |||
| - '@vocab' | |||
| - hydra | |||
| additionalProperties: true | |||
| userId: | |||
| type: integer | |||
| owner: | |||
| type: | |||
| - string | |||
| @@ -7027,11 +7160,8 @@ components: | |||
| - string | |||
| - 'null' | |||
| product: | |||
| type: | |||
| - string | |||
| - 'null' | |||
| format: iri-reference | |||
| example: 'https://example.com/' | |||
| description: '?ProductApi' | |||
| $ref: '#/components/schemas/Product' | |||
| productName: | |||
| readOnly: true | |||
| type: | |||
| @@ -7079,11 +7209,8 @@ components: | |||
| - string | |||
| - 'null' | |||
| product: | |||
| type: | |||
| - string | |||
| - 'null' | |||
| format: iri-reference | |||
| example: 'https://example.com/' | |||
| description: '?ProductApi' | |||
| $ref: '#/components/schemas/Product.jsonhal' | |||
| productName: | |||
| readOnly: true | |||
| type: | |||
| @@ -7145,11 +7272,8 @@ components: | |||
| - string | |||
| - 'null' | |||
| product: | |||
| type: | |||
| - string | |||
| - 'null' | |||
| format: iri-reference | |||
| example: 'https://example.com/' | |||
| description: '?ProductApi' | |||
| $ref: '#/components/schemas/Product.jsonld' | |||
| productName: | |||
| readOnly: true | |||
| type: | |||
| @@ -22,6 +22,7 @@ use ApiPlatform\Metadata\Delete; | |||
| use ApiPlatform\Metadata\Get; | |||
| use ApiPlatform\Metadata\GetCollection; | |||
| use ApiPlatform\Metadata\Post; | |||
| use Symfony\Component\PropertyInfo\Type; | |||
| use Symfony\Component\Serializer\Attribute\Groups; | |||
| use Symfony\Component\Validator\Constraints\NotBlank; | |||
| @@ -46,7 +47,11 @@ use Symfony\Component\Validator\Constraints\NotBlank; | |||
| processor: EntityClassDtoStateProcessor::class, | |||
| stateOptions: new Options(entityClass: PartnerFollow::class), | |||
| )] | |||
| #[ApiFilter(SearchFilter::class, properties: ['partner' => 'exact', 'user' => 'exact'])] | |||
| #[ApiFilter(SearchFilter::class, properties: [ | |||
| 'partner' => 'exact', | |||
| 'user' => 'exact', | |||
| 'partner.type' => 'exact' | |||
| ])] | |||
| class PartnerFollowApi implements OwnerInterface | |||
| { | |||
| #[ApiProperty(readable: false, writable: false, identifier: true)] | |||
| @@ -59,6 +64,20 @@ class PartnerFollowApi implements OwnerInterface | |||
| public ?string $userName = null; | |||
| #[NotBlank] | |||
| /** | |||
| * @var $partner ?PartnerApi | |||
| */ | |||
| #[ApiProperty( | |||
| writable: true, | |||
| readableLink: true, | |||
| writableLink: true, | |||
| builtinTypes: [ | |||
| new Type( | |||
| 'object', | |||
| class: PartnerApi::class, | |||
| ) | |||
| ] | |||
| )] | |||
| public ?PartnerApi $partner = null; | |||
| #[ApiProperty(writable: false)] | |||
| @@ -19,6 +19,7 @@ use ApiPlatform\Metadata\Delete; | |||
| use ApiPlatform\Metadata\Get; | |||
| use ApiPlatform\Metadata\GetCollection; | |||
| use ApiPlatform\Metadata\Post; | |||
| use Symfony\Component\PropertyInfo\Type; | |||
| use Symfony\Component\Validator\Constraints\NotBlank; | |||
| #[ApiResource( | |||
| @@ -42,19 +43,52 @@ use Symfony\Component\Validator\Constraints\NotBlank; | |||
| processor: EntityClassDtoStateProcessor::class, | |||
| stateOptions: new Options(entityClass: PartnerProduct::class), | |||
| )] | |||
| #[ApiFilter(SearchFilter::class, properties: ['partner' => 'exact', 'product' => 'exact', 'product.name' => 'ipartial'])] | |||
| #[ApiFilter(SearchFilter::class, properties: [ | |||
| 'partner' => 'exact', | |||
| 'product' => 'exact', | |||
| 'product.name' => 'ipartial', | |||
| 'partner.type' => 'exact' | |||
| ])] | |||
| class PartnerProductApi | |||
| { | |||
| #[ApiProperty(readable: false, writable: false, identifier: true)] | |||
| public ?int $id = null; | |||
| #[NotBlank] | |||
| /** | |||
| * @var $product ?PartnerApi | |||
| */ | |||
| #[ApiProperty( | |||
| writable: true, | |||
| readableLink: true, | |||
| writableLink: true, | |||
| builtinTypes: [ | |||
| new Type( | |||
| 'object', | |||
| class: PartnerApi::class, | |||
| ) | |||
| ] | |||
| )] | |||
| public ?PartnerApi $partner = null; | |||
| #[ApiProperty(writable: false)] | |||
| public ?string $partnerName = null; | |||
| #[NotBlank] | |||
| /** | |||
| * @var $product ?ProductApi | |||
| */ | |||
| #[ApiProperty( | |||
| writable: true, | |||
| readableLink: true, | |||
| writableLink: true, | |||
| builtinTypes: [ | |||
| new Type( | |||
| 'object', | |||
| class: ProductApi::class, | |||
| ) | |||
| ] | |||
| )] | |||
| public ?ProductApi $product = null; | |||
| #[ApiProperty(writable: false)] | |||
| @@ -14,6 +14,7 @@ use ApiPlatform\Metadata\ApiFilter; | |||
| use ApiPlatform\Metadata\ApiProperty; | |||
| use ApiPlatform\Metadata\ApiResource; | |||
| use App\Entity\Posting; | |||
| use App\Enum\PartnerType; | |||
| use App\Interface\OwnerInterface; | |||
| use App\State\EntityClassDtoStateProcessor; | |||
| use App\State\EntityToDtoStateProvider; | |||
| @@ -91,6 +92,9 @@ class PostingApi implements OwnerInterface | |||
| #[ApiProperty(writable: false)] | |||
| public ?string $partnerName = null; | |||
| #[ApiProperty(writable: false)] | |||
| public ?PartnerType $partnerType = null; | |||
| #[ApiProperty(writable: true)] | |||
| #[Groups(['posting:create'])] | |||
| #[Assert\NotBlank(groups: ['posting:create'])] | |||
| @@ -30,6 +30,7 @@ use Symfony\Component\Validator\Constraints\NotBlank; | |||
| #[ApiResource( | |||
| shortName: 'SaleSummary', | |||
| operations: [ | |||
| new Get(), | |||
| new GetCollection(), | |||
| ], | |||
| security: 'is_granted("ROLE_USER")', | |||
| @@ -37,6 +38,17 @@ use Symfony\Component\Validator\Constraints\NotBlank; | |||
| )] | |||
| class SaleSummary | |||
| { | |||
| #[ApiProperty(identifier: true)] | |||
| public int $userId; | |||
| /** | |||
| * @param int $userId | |||
| */ | |||
| public function __construct(int $userId) | |||
| { | |||
| $this->userId = $userId; | |||
| } | |||
| public ?UserApi $owner = null; | |||
| public ?string $ownerName = null; | |||
| @@ -44,4 +56,6 @@ class SaleSummary | |||
| public ?int $turnover = null; | |||
| public ?int $profit = null; | |||
| } | |||
| @@ -7,12 +7,12 @@ | |||
| namespace App\ApiResource; | |||
| use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; | |||
| use ApiPlatform\Doctrine\Orm\Filter\SearchFilter; | |||
| use ApiPlatform\Doctrine\Orm\State\Options; | |||
| use ApiPlatform\Metadata\ApiFilter; | |||
| use ApiPlatform\Metadata\ApiProperty; | |||
| use ApiPlatform\Metadata\ApiResource; | |||
| use App\Entity\PartnerProduct; | |||
| use App\Entity\UserProduct; | |||
| use App\Interface\OwnerInterface; | |||
| use App\State\EntityClassDtoStateProcessor; | |||
| @@ -21,13 +21,14 @@ use ApiPlatform\Metadata\Delete; | |||
| use ApiPlatform\Metadata\Get; | |||
| use ApiPlatform\Metadata\GetCollection; | |||
| use ApiPlatform\Metadata\Post; | |||
| use Symfony\Component\PropertyInfo\Type; | |||
| use Symfony\Component\Validator\Constraints\NotBlank; | |||
| #[ApiResource( | |||
| shortName: 'UserProduct', | |||
| operations: [ | |||
| new Get( | |||
| security: 'is_granted("ROLE_USER")' | |||
| security: 'is_granted("ROLE_USER")', | |||
| ), | |||
| new GetCollection( | |||
| security: 'is_granted("ROLE_USER")', | |||
| @@ -45,6 +46,7 @@ use Symfony\Component\Validator\Constraints\NotBlank; | |||
| stateOptions: new Options(entityClass: UserProduct::class), | |||
| )] | |||
| #[ApiFilter(SearchFilter::class, properties: ['user' => 'exact', 'product' => 'exact'])] | |||
| #[ApiFilter(OrderFilter::class, properties: ['product.name'], arguments: ['orderParameterName' => 'order'])] | |||
| class UserProductApi implements OwnerInterface | |||
| { | |||
| #[ApiProperty(readable: false, writable: false, identifier: true)] | |||
| @@ -57,6 +59,20 @@ class UserProductApi implements OwnerInterface | |||
| public ?string $userName = null; | |||
| #[NotBlank] | |||
| /** | |||
| * @var $product ?ProductApi | |||
| */ | |||
| #[ApiProperty( | |||
| writable: true, | |||
| readableLink: true, | |||
| writableLink: true, | |||
| builtinTypes: [ | |||
| new Type( | |||
| 'object', | |||
| class: ProductApi::class, | |||
| ) | |||
| ] | |||
| )] | |||
| public ?ProductApi $product = null; | |||
| #[ApiProperty(writable: false)] | |||
| @@ -53,6 +53,7 @@ class PostingEntityToApiMapper implements MapperInterface | |||
| MicroMapperInterface::MAX_DEPTH => 1, | |||
| ]); | |||
| $dto->partnerName = $entity->getPartner()?->getName(); | |||
| $dto->partnerType = $entity->getPartner()?->getType(); | |||
| $dto->product = $this->microMapper->map($entity->getProduct(), ProductApi::class, [ | |||
| MicroMapperInterface::MAX_DEPTH => 1, | |||
| @@ -38,10 +38,11 @@ class SaleSummaryStateProvider implements ProviderInterface | |||
| count($salesSummaries), | |||
| ); | |||
| } | |||
| $salesSummaries = $this->createSaleSummaries(0); | |||
| return $this->createSaleSummaries(0, count($salesSummaries)); | |||
| $user = $this->userRepository->find($uriVariables['userId']); | |||
| if ($user !== null) { | |||
| return $this->createSaleSummary($user); | |||
| } | |||
| return null; | |||
| } | |||
| private function createSaleSummaries(int $offset, int $limit = 50): array | |||
| @@ -50,24 +51,33 @@ class SaleSummaryStateProvider implements ProviderInterface | |||
| $salesSummaries = []; | |||
| /** @var User $user */ | |||
| foreach ($users as $user) { | |||
| $sales = $user->getSales(); | |||
| if (count($sales) > 0) { | |||
| $salesSummary = new SaleSummary(); | |||
| $salesSummary->owner = $this->microMapper->map($user, UserApi::class); | |||
| $salesSummary->ownerName = $user->getFirstName() . ' ' . $user->getLastName(); | |||
| $salesSummary->turnover = 0; | |||
| $salesSummary->profit = 0; | |||
| foreach ($sales as $sale) { | |||
| $salesSummary->turnover += $sale->getTurnover(); | |||
| $salesSummary->profit += $sale->getProfit(); | |||
| } | |||
| $salesSummary = $this->createSaleSummary($user); | |||
| if ($salesSummary !== null) { | |||
| $salesSummaries[] = $salesSummary; | |||
| } | |||
| $sales = $user->getSales(); | |||
| } | |||
| usort($salesSummaries, function(SaleSummary $a, SaleSummary $b) { | |||
| return $b->turnover - $a->turnover; | |||
| }); | |||
| return $salesSummaries; | |||
| } | |||
| private function createSaleSummary(User $user) | |||
| { | |||
| $sales = $user->getSales(); | |||
| if (count($sales) > 0) { | |||
| $salesSummary = new SaleSummary($user->getId()); | |||
| $salesSummary->owner = $this->microMapper->map($user, UserApi::class); | |||
| $salesSummary->ownerName = $user->getFirstName() . ' ' . $user->getLastName(); | |||
| $salesSummary->turnover = 0; | |||
| $salesSummary->profit = 0; | |||
| foreach ($sales as $sale) { | |||
| $salesSummary->turnover += $sale->getTurnover(); | |||
| $salesSummary->profit += $sale->getProfit(); | |||
| } | |||
| return $salesSummary; | |||
| } | |||
| return null; | |||
| } | |||
| } | |||