<?php
namespace App\Model\Product;
use ApiPlatform\Core\Annotation\ApiFilter;
use ApiPlatform\Core\Annotation\ApiProperty;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Model\Category\Category;
use App\Model\CustomerFile\CustomerFile;
use App\Model\IriNormalizableInterface;
use App\Model\NormalizeAsIRITrait;
use App\Model\Params\Params;
use App\Model\ProductSpecificField\ProductSpecificField;
use App\Model\SpecificField\SpecificField;
use App\Model\SpecificFieldsAwareInterface;
use App\Model\Traits\ImportableObjectTrait;
use App\Model\Traits\TimestampableTrait;
use App\Model\Unit\Unit;
use App\Model\Vat\Vat;
use App\Model\Warranty\Warranty;
use App\V4\Entity\ActionableEntityInterface;
use App\V4\Model\ProductConfiguration\ProductConfiguration;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Serializer\Annotation\MaxDepth;
use Symfony\Component\Serializer\Annotation\SerializedName;
use Symfony\Component\Validator\Constraints as Assert;
//Ici le formulaire d'édition est en POST, car il nécessite du form-data qui ne fonctionne pas avec PATCH ou PUT
/**
* @ApiResource(
* attributes={
* "api_allow_update": true
* },
* collectionOperations={
* "get": {
* "normalization_context": {
* "groups": {"product:list"}
* },
* "security": "is_granted(constant('App\\Voters\\ProductVoter::PRODUCT_SHOW_LIST'), 'App\Voters\ProductVoter')"
* },
* "post": {
* "deserialize": false,
* "write": false,
* "controller": "App\Controller\Product\ProductDataPersisterAction",
* "input_formats": {
* "multipart": {"multipart/form-data"}
* },
* "normalization_context": {
* "groups": {"product:read"}
* },
* "denormalization_context": {
* "groups": {}
* }
* },
* "get_form": {
* "method": "GET",
* "path": "/products/form",
* "controller": App\Controller\Product\GetProductFormAction::class
* },
* "get_search": {
* "method": "GET",
* "path": "/products/search/form",
* "controller": App\Controller\Product\GetProductSearchFormAction::class,
* },
* "post_search": {
* "method": "POST",
* "deserialize": false,
* "path": "/products/search/form",
* "normalization_context": {
* "groups": {"product:list"}
* },
* "controller": App\Controller\Product\PostProductSearchFormAction::class,
* "validate": false,
* },
* "post_export_request": {
* "method": "POST",
* "path": "/v4/export/product",
* "controller": App\V4\Action\Export\EntityExportRequestAction::class,
* "swagger_context": {
* "summary": "Demande de création d'export de produits",
* "description": "Demande de création d'export de produits où 'ids' sont les ids des produits et 'fields' les champs souhaités",
* "parameters": {
* {
* "in": "body",
* "name": "body",
* "schema": {
* "type": "object",
* "properties": {
* "ids": {
* "type": "array",
* "items": {"type": "string"}
* },
* "fields": {
* "type": "array",
* "items": {"type": "string"}
* }
* }
* }
* }
* },
* "responses": {
* "201": {
* "description": "Création de l'export avec succès",
* "schema": {
* "type": "object",
* "properties": {
* "message": {"type": "string", "example": "export_request_created"},
* }
* }
* },
* "400": {
* "description": "Utilisateur non trouvé ou champs 'fields' ou 'ids' manquant dans le body request",
* "schema": {
* "type": "object",
* "properties": {
* "message": {"type": "string", "example": "user_not_found or missing_fields_or_ids"},
* }
* }
* },
* "422": {
* "description": "Les champs 'fields' ou 'ids' du body request sont vides ou l'entité demandée est invalide",
* "schema": {
* "type": "object",
* "properties": {
* "message": {"type": "string", "example": "empty_fields_or_ids or entity_not_valid"},
* }
* }
* },
* },
* }
* },
* "post_export_download": {
* "method": "POST",
* "path": "/v4/export/download/product/{id}",
* "controller": App\V4\Action\Export\EntityExportDownloadAction::class,
* "swagger_context": {
* "summary": "Demande de téléchargement d'un export produits",
* "description": "Demande de téléchargement d'un export produits ou 'id' est le nom du fichier d'export",
* "requestBody": {},
* "parameters": {
* {
* "in": "path",
* "name": "id",
* "type": "string",
* "required": "true"
* }
* },
* "responses": {
* "200": {
* "description": "Téléchargement de l'export réalisé avec succès"
* },
* "401": {
* "description": "Utilisateur non trouvé ou non connecté",
* "schema": {
* "type": "object",
* "properties": {
* "message": {"type": "string", "example": "need_authenticated"},
* }
* }
* },
* "404": {
* "description": "Fichier introuvable",
* "schema": {
* "type": "object",
* "properties": {
* "message": {"type": "string", "example": "export_not_found"},
* }
* }
* },
* },
* }
* }
* },
* itemOperations={
* "get": {
* "normalization_context": {
* "groups": {"product:read"}
* },
* "security": "is_granted(constant('App\\Voters\\ProductVoter::PRODUCT_ADD_EDIT'), 'App\Voters\ProductVoter')"
* },
* "get_form": {
* "method": "GET",
* "path": "/products/{id}/form",
* "controller": App\Controller\Product\GetProductFormAction::class
* },
* "put": {
* "deserialize": false,
* "write": false,
* "method": "POST",
* "controller": "App\Controller\Product\ProductDataPersisterAction",
* "input_formats": {
* "multipart": {"multipart/form-data"}
* },
* "normalization_context": {
* "groups": {"product:read"}
* },
* "denormalization_context": {
* "groups": {}
* }
* },
* "delete"
* }
* )
*
* @ApiFilter(App\Filters\PagingFilter::class)
*/
class Product implements IriNormalizableInterface, SpecificFieldsAwareInterface, ActionableEntityInterface
{
public const SPECIFIC_FIELD_CLASS_NAME = ProductSpecificField::class;
public const SUBSCRIPTION_UNIT_HOUR = 'hours';
public const SUBSCRIPTION_UNIT_DAY = 'days';
public const SUBSCRIPTION_UNIT_MONTH = 'months';
public const SUBSCRIPTION_UNIT_YEAR = 'years';
public const SUBSCRIPTION_UNITS = [
self::SUBSCRIPTION_UNIT_HOUR,
self::SUBSCRIPTION_UNIT_DAY,
self::SUBSCRIPTION_UNIT_MONTH,
self::SUBSCRIPTION_UNIT_YEAR,
];
use TimestampableTrait;
use ImportableObjectTrait;
use NormalizeAsIRITrait;
/**
* @var string|null
*
* @ApiProperty(identifier=true)
* @Groups({"list", "product:list", "product:read"})
*/
private $id;
/**
* @var string|null
*
* @Groups({"product:list", "product:read"})
*/
private $externalId;
/**
* @var string|null
*/
private $customerId;
/**
* @var Vat|null
*
* @Groups({"product:list", "product:read", "product:write", "product:update"})
*/
private $vat;
/**
* @var Unit|null
*
* @Groups({"product:list", "product:read", "product:write", "product:update"})
*/
private $unit;
/**
* @var Category[]|null
*
* @Groups({"list", "product:list", "product:read", "product:write", "product:update"})
* @MaxDepth(1)
*/
private $categories;
/**
* @var Warranty|null
*
* @Groups({"product:list", "product:read", "product:write", "product:update"})
*/
private $warranty;
/**
* @var SpecificField[]|Collection
*
* @Groups({"product:list", "product:read", "product:write", "product:update"})
*/
private $specificFields;
/**
* @var string|null
*
* @Assert\Length(max="255")
* @Assert\NotBlank()
*
* @Groups({"list", "product:list", "product:read", "product:write", "product:update"})
*/
private $name;
/**
* @var string|null
*
* @Assert\Length(max="255")
*
* @Groups({"list", "product:list", "product:read", "product:write", "product:update"})
*/
private $shortDescription;
/**
* @var string|null
*
* @Assert\Length(max="255")
*
* @Groups({"list", "product:list", "product:read", "product:write", "product:update"})
*/
private $reference;
/**
* @var string|null
*
* @Groups({"product:list", "product:read", "product:write", "product:update"})
*/
private $priceExcludingVat;
/**
* @var string|null
*
* @Groups({"product:list", "product:read", "product:write", "product:update"})
*/
private $sellingPriceExcludingVat;
/**
* @var string|null
*
* @Groups({"product:list", "product:read", "product:write", "product:update"})
*/
private $imageId;
/**
* @var CustomerFile|null
*
* @Groups({"product:list", "product:read", "product:write", "product:update"})
*/
private $imageFile;
/**
* @var string|null
*
* @Assert\Length(max="255")
*
* @Groups({"product:list", "product:read", "product:write", "product:update"})
*/
private $serialNumber;
/**
* @var bool
*
* @Groups({"product:list", "product:read", "product:write", "product:update"})
*/
private $isComponent = false;
/**
* @var int|null
*
* @Assert\GreaterThan(0)
*
* @Groups({"product:list", "product:read", "product:write", "product:update"})
*/
private $stockQty = 1;
/**
* @var CustomerFile[]
*
* @Groups({"product:list", "product:read", "product:write", "product:update"})
*/
private $customerFiles;
/**
* @var ProductConfiguration|null
*
* @Groups({"list", "product:list", "product:read", "product:write", "product:update"})
*/
private $productConfiguration;
/**
* @var int|null
*
* @Groups({
* "list",
* "product:list", "product:read",
* "product:write", "product:update",
* })
*/
private $subscriptionValue;
/**
* @var string|null
*
* @Groups({
* "list",
* "product:list", "product:read",
* "product:write", "product:update",
* })
*/
private $subscriptionUnit;
/**
* @var string|null
*
* @Groups({
* "list",
* "product:list", "product:read",
* "product:write", "product:update",
* })
*/
private $quoteSection;
/**
* @var string|null
*
* @Groups({
* "list",
* "product:list", "product:read",
* "product:write", "product:update",
* })
*/
private $description;
public function __construct()
{
$this->specificFields = new ArrayCollection();
$this->categories = new ArrayCollection();
}
/**
* @Assert\IsTrue(message="subscription_unit_empty_and_not_value");
**/
public function isCorrectSubscriptionValueAndUnit(): bool
{
if (!$this->isSubscriptionProduct()) {
return true;
}
return null !== $this->subscriptionValue && null !== $this->subscriptionUnit;
}
/**
* @return bool
*/
public function isSubscriptionProduct(): bool
{
return null !== $this->subscriptionValue || null !== $this->subscriptionUnit;
}
/**
* @return string
*/
public function getId(): ?string
{
return $this->id;
}
/**
* @param string|null $id
*
* @return self
*/
public function setId(?string $id): self
{
$this->id = $id;
return $this;
}
/**
* @return string|null
*/
public function getExternalId(): ?string
{
return $this->externalId;
}
/**
* @param string|null $externalId
*
* @return self
*/
public function setExternalId(?string $externalId): self
{
$this->externalId = $externalId;
return $this;
}
/**
* @return string|null
*/
public function getCustomerId(): ?string
{
return $this->customerId;
}
/**
* @param string $customerId
*
* @return self
*/
public function setCustomerId(string $customerId): self
{
$this->customerId = $customerId;
return $this;
}
/**
* @return Params|null
*/
public function getVat(): ?Params
{
return $this->vat;
}
/**
* @param Params|null $vat
*
* @return self
*/
public function setVat(?Params $vat): self
{
$this->vat = $vat;
return $this;
}
/**
* @return Params|null
*/
public function getUnit(): ?Params
{
return $this->unit;
}
/**
* @param Params|null $unit
*
* @return self
*/
public function setUnit(?Params $unit): self
{
$this->unit = $unit;
return $this;
}
/**
* @return Collection|Category[]
*/
public function getCategories(): Collection
{
return $this->categories;
}
/**
* @param Collection|Category[] $categories
*
* @return $this
*/
public function setCategories(Collection $categories): self
{
$this->categories = $categories;
return $this;
}
/**
* @param Category $category
*
* @return $this
*/
public function addCategory(Category $category): self
{
if (!$this->categories->contains($category)) {
$this->categories[] = $category;
$category->addProduct($this);
}
return $this;
}
/**
* @param Category $category
*
* @return $this
*/
public function removeCategory(Category $category): self
{
if ($this->categories->contains($category)) {
$this->categories->removeElement($category);
$category->removeProduct($this);
}
return $this;
}
/**
* @return Params|null
*/
public function getWarranty(): ?Params
{
return $this->warranty;
}
/**
* @param Params|null $warranty
*
* @return self
*/
public function setWarranty(?Params $warranty): self
{
$this->warranty = $warranty;
return $this;
}
/**
* {@inheritdoc}
*/
public function getSpecificFields(): Collection
{
return $this->specificFields;
}
/**
* {@inheritdoc}
*/
public function getSpecificFieldByFieldId(string $id): ?SpecificField
{
foreach ($this->specificFields as $specificField) {
if ($specificField->getFieldId() === $id) {
return $specificField;
}
}
return null;
}
/**
* @param ProductSpecificField $specificField
*
* @return $this
*
* @see ImportableObjectTrait::class
*/
public function addProductSpecificField(ProductSpecificField $specificField): self
{
return $this->addSpecificField($specificField);
}
/**
* {@inheritdoc}
*/
public function addSpecificField($specificField): SpecificFieldsAwareInterface
{
$this->specificFields[] = $specificField;
return $this;
}
/**
* {@inheritdoc}
*/
public function setSpecificFields(Collection $specificFields): self
{
$this->specificFields = $specificFields;
return $this;
}
/**
* @return string|null
*/
public function getName(): ?string
{
return $this->name;
}
/**
* @param string|null $name
*
* @return self
*/
public function setName(?string $name): self
{
$this->name = $name;
return $this;
}
/**
* @return string|null
*/
public function getShortDescription(): ?string
{
return $this->shortDescription;
}
/**
* @param string|null $shortDescription
*
* @return Product
*/
public function setShortDescription(?string $shortDescription): Product
{
$this->shortDescription = $shortDescription;
return $this;
}
/**
* @return string|null
*/
public function getReference(): ?string
{
return $this->reference;
}
/**
* @param string|null $reference
*
* @return self
*/
public function setReference(?string $reference): self
{
$this->reference = $reference;
return $this;
}
/**
* @return string|null
*/
public function getPriceExcludingVat(): ?string
{
return $this->priceExcludingVat;
}
/**
* @param string|null $priceExcludingVat
*
* @return self
*/
public function setPriceExcludingVat(?string $priceExcludingVat): self
{
$this->priceExcludingVat = $priceExcludingVat;
return $this;
}
/**
* @return string|null
*/
public function getSellingPriceExcludingVat(): ?string
{
return $this->sellingPriceExcludingVat;
}
/**
* @param string|null $sellingPriceExcludingVat
*
* @return self
*/
public function setSellingPriceExcludingVat(?string $sellingPriceExcludingVat): self
{
$this->sellingPriceExcludingVat = $sellingPriceExcludingVat;
return $this;
}
/**
* @return string|null
*/
public function getImageId(): ?string
{
return $this->imageId;
}
/**
* @param string|null $imageId
*
* @return self
*/
public function setImageId(?string $imageId): self
{
$this->imageId = $imageId;
return $this;
}
/**
* @return CustomerFile|null
*/
public function getImageFile(): ?CustomerFile
{
return $this->imageFile;
}
/**
* @param CustomerFile|null $imageFile
*
* @return Product
*/
public function setImageFile(?CustomerFile $imageFile): self
{
$this->imageFile = $imageFile;
return $this;
}
/**
* @TODO: PV-746: Find a better way
*
* @SerializedName("imageUrl")
* @Groups({"list", "product:list", "product:read"})
*
* @return string
*/
public function getImageUrl(): ?string
{
if ($this->imageId) {
return '/api/logo/'.$this->imageId;
}
return null;
}
/**
* @return string|null
*/
public function getSerialNumber(): ?string
{
return $this->serialNumber;
}
/**
* @param string|null $serialNumber
*
* @return self
*/
public function setSerialNumber(?string $serialNumber): self
{
$this->serialNumber = $serialNumber;
return $this;
}
/**
* @return bool
*/
public function isComponent(): bool
{
return $this->isComponent;
}
/**
* @param bool $isComponent
*
* @return self
*/
public function setIsComponent(bool $isComponent): self
{
$this->isComponent = $isComponent;
return $this;
}
/**
* @return int|null
*/
public function getStockQty(): ?int
{
return $this->stockQty;
}
/**
* @param int|null $stockQty
*
* @return self
*/
public function setStockQty(?int $stockQty): self
{
$this->stockQty = $stockQty;
return $this;
}
/**
* @return CustomerFile[]
*/
public function getCustomerFiles()
{
return $this->customerFiles;
}
/**
* @param CustomerFile $customerFile
*
* @return $this
*/
public function addCustomerFile(CustomerFile $customerFile): self
{
$this->customerFiles[] = $customerFile;
return $this;
}
/**
* @param CustomerFile $customerFile
*
* @return self
*/
public function removeCustomerFile(CustomerFile $customerFile): self
{
$index = array_search($customerFile, $this->customerFiles, true);
if (false !== $index) {
unset($this->customerFiles[$index]);
}
return $this;
}
/**
* @param CustomerFile[] $customerFiles
*
* @return Product
*/
public function setCustomerFiles(array $customerFiles): self
{
$this->customerFiles = $customerFiles;
return $this;
}
/**
* @return ProductConfiguration|null
*/
public function getProductConfiguration(): ?ProductConfiguration
{
return $this->productConfiguration;
}
/**
* @param ProductConfiguration|null $productConfiguration
*
* @return Product
*/
public function setProductConfiguration(?ProductConfiguration $productConfiguration): self
{
$this->productConfiguration = $productConfiguration;
return $this;
}
/**
* @return int|null
*/
public function getSubscriptionValue(): ?int
{
return $this->subscriptionValue;
}
/**
* @param int|null $subscriptionValue
*
* @return self
*/
public function setSubscriptionValue(?int $subscriptionValue): self
{
$this->subscriptionValue = $subscriptionValue;
return $this;
}
/**
* @return string|null
*/
public function getSubscriptionUnit(): ?string
{
return $this->subscriptionUnit;
}
/**
* @param string|null $subscriptionUnit
*
* @return self
*/
public function setSubscriptionUnit(?string $subscriptionUnit): self
{
$this->subscriptionUnit = $subscriptionUnit;
return $this;
}
/**
* @return string|null
*/
public function getQuoteSection(): ?string
{
return $this->quoteSection;
}
/**
* @param string|null $quoteSection
*
* @return Product
*/
public function setQuoteSection(?string $quoteSection): self
{
$this->quoteSection = $quoteSection;
return $this;
}
/**
* @Groups({
* "quote:read"
* })
*
* @return bool
*/
public function isConfigurable(): bool
{
return $this->getProductConfiguration() instanceof ProductConfiguration;
}
/**
* @return string|null
*/
public function getDescription(): ?string
{
return $this->description;
}
/**
* @param string|null $description
*
* @return $this
*/
public function setDescription(?string $description): self
{
$this->description = $description;
return $this;
}
}