Split Part entity class into multiple traits.

The part class has become very big and clumsy, that way it should be easier to maintain this entity...
This commit is contained in:
Jan Böhmer 2019-09-16 21:40:47 +02:00
parent 3ecbe19fd6
commit f7c2f1032f
8 changed files with 1041 additions and 774 deletions

View file

@ -64,6 +64,12 @@ namespace App\Entity\Parts;
use App\Entity\Attachments\Attachment;
use App\Entity\Attachments\AttachmentContainingDBElement;
use App\Entity\Devices\Device;
use App\Entity\Parts\PartTraits\AdvancedPropertyTrait;
use App\Entity\Parts\PartTraits\MasterAttachmentTrait;
use App\Entity\Parts\PartTraits\BasicPropertyTrait;
use App\Entity\Parts\PartTraits\InstockTrait;
use App\Entity\Parts\PartTraits\ManufacturerTrait;
use App\Entity\Parts\PartTraits\OrderTrait;
use App\Entity\PriceInformations\Orderdetail;
use App\Security\Annotations\ColumnSecurity;
use App\Validator\Constraints\Selectable;
@ -79,78 +85,26 @@ use Symfony\Component\Validator\Constraints as Assert;
* DONT USE orphanRemoval on properties with ColumnSecurity!! An empty collection will be created as placeholder,
* and the partlots are deleted, even if we want dont want that!
*
* The class properties are split over various traits in directory PartTraits.
* Otherwise this class would be too big, to be maintained.
*
* @ORM\Entity(repositoryClass="App\Repository\PartRepository")
* @ORM\Table("`parts`")
*/
class Part extends AttachmentContainingDBElement
{
public const INSTOCK_UNKNOWN = -2;
/**
* @ORM\OneToMany(targetEntity="App\Entity\Attachments\PartAttachment", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=false)
* @ColumnSecurity(type="collection", prefix="attachments")
* @Assert\Valid()
*/
protected $attachments;
/**
* @var Category
* @ORM\ManyToOne(targetEntity="Category", inversedBy="parts")
* @ORM\JoinColumn(name="id_category", referencedColumnName="id", nullable=false)
* @ColumnSecurity(prefix="category", type="App\Entity\Parts\Category")
* @Selectable()
*/
protected $category;
/**
* @var Footprint|null
* @ORM\ManyToOne(targetEntity="Footprint", inversedBy="parts")
* @ORM\JoinColumn(name="id_footprint", referencedColumnName="id")
* @ColumnSecurity(prefix="footprint", type="App\Entity\Parts\Footprint")
* @Selectable()
*/
protected $footprint;
/**
* @var Manufacturer|null
* @ORM\ManyToOne(targetEntity="Manufacturer", inversedBy="parts")
* @ORM\JoinColumn(name="id_manufacturer", referencedColumnName="id")
* @ColumnSecurity(prefix="manufacturer", type="App\Entity\Parts\Manufacturer")
* @Selectable()
*/
protected $manufacturer;
/**
* @var Attachment
* @ORM\ManyToOne(targetEntity="App\Entity\Attachments\Attachment")
* @ORM\JoinColumn(name="id_master_picture_attachement", referencedColumnName="id")
* @Assert\Expression("value == null or value.isPicture()", message="part.master_attachment.must_be_picture")
* @ColumnSecurity(prefix="attachments", type="object")
*/
protected $master_picture_attachment;
/**
* @var Orderdetail[]
* @ORM\OneToMany(targetEntity="App\Entity\PriceInformations\Orderdetail", mappedBy="part", cascade={"persist", "remove"}, orphanRemoval=false)
* @Assert\Valid()
* @ColumnSecurity(prefix="orderdetails", type="collection")
*/
protected $orderdetails;
/**
* @var Orderdetail
* @ORM\OneToOne(targetEntity="App\Entity\PriceInformations\Orderdetail")
* @ORM\JoinColumn(name="order_orderdetails_id", referencedColumnName="id")
*
* @ColumnSecurity(prefix="order", type="object")
*/
protected $order_orderdetail;
use AdvancedPropertyTrait;
use MasterAttachmentTrait;
use BasicPropertyTrait;
use InstockTrait;
use ManufacturerTrait;
use OrderTrait;
//TODO
protected $devices;
/**
* @ColumnSecurity(type="datetime")
* @ColumnSecurity(type="datetime")
* @ORM\Column(type="datetime", name="datetime_added", options={"default"="CURRENT_TIMESTAMP"})
*/
protected $addedDate;
@ -162,128 +116,12 @@ class Part extends AttachmentContainingDBElement
*/
protected $lastModified;
/**********************
* Propertys
***********************/
/**
* @var string
* @ORM\Column(type="string")
*
* @ColumnSecurity(prefix="name")
*/
protected $name = '';
/**
* @var string
* @ORM\Column(type="text")
* @ColumnSecurity(prefix="description")
*/
protected $description = '';
/**
* @var ?PartLot[]
* @ORM\OneToMany(targetEntity="PartLot", mappedBy="part", cascade={"persist", "remove"}, orphanRemoval=false)
* @ORM\OneToMany(targetEntity="App\Entity\Attachments\PartAttachment", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=false)
* @ColumnSecurity(type="collection", prefix="attachments")
* @Assert\Valid()
* @ColumnSecurity(type="collection", prefix="lots")
*/
protected $partLots;
/**
* @var float
* @ORM\Column(type="float")
* @Assert\PositiveOrZero()
*
* @ColumnSecurity(prefix="mininstock", type="integer")
*/
protected $minamount = 0;
/**
* @var string
* @ORM\Column(type="text")
* @ColumnSecurity(prefix="comment")
*/
protected $comment = '';
/**
* @var bool
* @ORM\Column(type="boolean")
*/
protected $visible = true;
/**
* @var bool
* @ORM\Column(type="boolean")
* @ColumnSecurity(type="boolean")
*/
protected $favorite = false;
/**
* @var int
* @ORM\Column(type="integer")
* @ColumnSecurity(prefix="order", type="integer")
*/
protected $order_quantity = 0;
/**
* @var bool
* @ORM\Column(type="boolean")
* @ColumnSecurity(prefix="order", type="boolean")
*/
protected $manual_order = false;
/**
* @var string
* @ORM\Column(type="string")
* @Assert\Url()
* @ColumnSecurity(prefix="mpn", type="string", placeholder="")
*/
protected $manufacturer_product_url = '';
/**
* @var string
* @ORM\Column(type="string")
* @ColumnSecurity(prefix="mpn", type="string", placeholder="")
*/
protected $manufacturer_product_number = '';
/**
* @var string
* @ORM\Column(type="string", length=255, nullable=true)
* @Assert\Choice({"announced", "active", "nrfnd", "eol", "discontinued", ""})
* @ColumnSecurity(type="string", prefix="status", placeholder="")
*/
protected $manufacturing_status = "";
/**
* @var bool Determines if this part entry needs review (for example, because it is work in progress)
* @ORM\Column(type="boolean")
* @ColumnSecurity(type="boolean")
*/
protected $needs_review = false;
/**
* @var ?MeasurementUnit The unit in which the part's amount is measured.
* @ORM\ManyToOne(targetEntity="MeasurementUnit", inversedBy="parts")
* @ORM\JoinColumn(name="id_part_unit", referencedColumnName="id", nullable=true)
* @ColumnSecurity(type="object", prefix="unit")
*/
protected $partUnit;
/**
* @var string A comma seperated list of tags, assocciated with the part.
* @ORM\Column(type="text")
* @ColumnSecurity(type="string", prefix="tags", placeholder="")
*/
protected $tags = '';
/**
* @var float|null How much a single part unit weighs in gramms.
* @ORM\Column(type="float", nullable=true)
* @ColumnSecurity(type="float", placeholder=null)
* @Assert\PositiveOrZero()
*/
protected $mass;
protected $attachments;
public function __construct()
{
@ -303,280 +141,6 @@ class Part extends AttachmentContainingDBElement
return 'P' . sprintf('%06d', $this->getID());
}
/*********************************************************************************
* Getters
********************************************************************************/
/**
* Get the description string like it is saved in the database.
* This can contain BBCode, it is not parsed yet.
*
* @return string the description
*/
public function getDescription(): string
{
return htmlspecialchars($this->description);
}
/**
* Get the count of parts which must be in stock at least.
* If a integer-based part unit is selected, the value will be rounded to integers
*
* @return float count of parts which must be in stock at least
*/
public function getMinAmount(): float
{
if ($this->useFloatAmount()) {
return $this->minamount;
}
return round($this->minamount);
}
/**
* Get the comment associated with this part.
*
* @return string The raw/unparsed comment
*/
public function getComment(): string
{
return htmlspecialchars($this->comment);
}
/**
* Get if this part is obsolete.
*
* A Part is marked as "obsolete" if all their orderdetails are marked as "obsolete".
* If a part has no orderdetails, the part isn't marked as obsolete.
*
* @return bool true, if this part is obsolete. false, if this part isn't obsolete
*/
public function isObsolete(): bool
{
$all_orderdetails = $this->getOrderdetails();
if (0 === count($all_orderdetails)) {
return false;
}
foreach ($all_orderdetails as $orderdetails) {
if (!$orderdetails->getObsolete()) {
return false;
}
}
return true;
}
/**
* Get if this part is visible.
*
* @return bool true if this part is visible
* false if this part isn't visible
*/
public function isVisible(): bool
{
return $this->visible;
}
/**
* Get if this part is a favorite.
*
* @return bool * true if this part is a favorite
* * false if this part is not a favorite.
*/
public function isFavorite(): bool
{
return $this->favorite;
}
/**
* Get the selected order orderdetails of this part.
* @return Orderdetail the selected order orderdetails
*/
public function getOrderOrderdetails(): ?Orderdetail
{
return $this->order_orderdetail;
}
/**
* Get the order quantity of this part.
*
* @return int the order quantity
*/
public function getOrderQuantity(): int
{
return $this->order_quantity;
}
/**
* Check if this part is marked for manual ordering.
*
* @return bool the "manual_order" attribute
*/
public function isMarkedForManualOrder(): bool
{
return $this->manual_order;
}
/**
* Get the link to the website of the article on the manufacturers website
* When no this part has no explicit url set, then it is tried to generate one from the Manufacturer of this part
* automatically.
*
* @param
*
* @return string the link to the article
*/
public function getManufacturerProductUrl(): string
{
if ('' !== $this->manufacturer_product_url) {
return $this->manufacturer_product_url;
}
if (null !== $this->getManufacturer()) {
return $this->getManufacturer()->getAutoProductUrl($this->name);
}
return ''; // no url is available
}
/**
* Similar to getManufacturerProductUrl, but here only the database value is returned.
*
* @return string The manufacturer url saved in DB for this part.
*/
public function getCustomProductURL(): string
{
return $this->manufacturer_product_url;
}
/**
* Returns the manufacturing/production status for this part.
* The status can be one of the following:
* (Similar to https://designspark.zendesk.com/hc/en-us/articles/213584805-What-are-the-Lifecycle-Status-definitions-)
* * "": Status unknown
* * "announced": Part has been announced, but is not in production yet
* * "active": Part is in production and will be for the forseeable future
* * "nrfnd": Not recommended for new designs.
* * "eol": Part will become discontinued soon
* * "discontinued": Part is obsolete/discontinued by the manufacturer
* @return string
*/
public function getManufacturingStatus(): ?string
{
return $this->manufacturing_status;
}
/**
* Sets the manufacturing status for this part
* See getManufacturingStatus() for valid values.
* @param string $manufacturing_status
* @return Part
*/
public function setManufacturingStatus(string $manufacturing_status): Part
{
$this->manufacturing_status = $manufacturing_status;
return $this;
}
/**
* Get the category of this part.
*
* There is always a category, for each part!
*
* @return Category the category of this part
*/
public function getCategory(): ?Category
{
return $this->category;
}
/**
* Get the footprint of this part (if there is one).
*
* @return Footprint the footprint of this part (if there is one)
*/
public function getFootprint(): ?Footprint
{
return $this->footprint;
}
/**
* Get the manufacturer of this part (if there is one).
*
* @return Manufacturer the manufacturer of this part (if there is one)
*/
public function getManufacturer(): ?Manufacturer
{
return $this->manufacturer;
}
/**
* Get the master picture "Attachment"-object of this part (if there is one).
* The master picture should be used as a visual description/representation of this part.
*
* @return Attachment the master picture Attachement of this part (if there is one)
*/
public function getMasterPictureAttachment(): ?Attachment
{
return $this->master_picture_attachment;
}
/**
* Sets the new master picture for this part.
* @param Attachment|null $new_master_attachment
* @return Part
*/
public function setMasterPictureAttachment(?Attachment $new_master_attachment): Part
{
$this->master_picture_attachment = $new_master_attachment;
return $this;
}
/**
* Get all orderdetails of this part.
*
* @param bool $hide_obsolete If true, obsolete orderdetails will NOT be returned
*
* @return Collection|Orderdetail[] * all orderdetails as a one-dimensional array of Orderdetails objects
* (empty array if there are no ones)
* * the array is sorted by the suppliers names / minimum order quantity
*
*/
public function getOrderdetails(bool $hide_obsolete = false) : Collection
{
//If needed hide the obsolete entries
if ($hide_obsolete) {
$orderdetails = $this->orderdetails;
foreach ($orderdetails as $key => $details) {
if ($details->getObsolete()) {
unset($orderdetails[$key]);
}
}
return $orderdetails;
}
return $this->orderdetails;
}
public function addOrderdetail(Orderdetail $orderdetail) : Part
{
$orderdetail->setPart($this);
$this->orderdetails->add($orderdetail);
return $this;
}
public function removeOrderdetail(Orderdetail $orderdetail) : Part
{
$this->orderdetails->removeElement($orderdetail);
return $this;
}
/**
* Get all devices which uses this part.
*
@ -590,322 +154,4 @@ class Part extends AttachmentContainingDBElement
return $this->devices;
}
/**
* Checks if this part is marked, for that it needs further review.
* @return bool
*/
public function isNeedsReview(): bool
{
return $this->needs_review;
}
/**
* Sets the "needs review" status of this part.
* @param bool $needs_review
* @return Part
*/
public function setNeedsReview(bool $needs_review): Part
{
$this->needs_review = $needs_review;
return $this;
}
/**
* Get all part lots where this part is stored.
* @return PartLot[]|Collection
*/
public function getPartLots() : Collection
{
return $this->partLots;
}
public function addPartLot(PartLot $lot): Part
{
$lot->setPart($this);
$this->partLots->add($lot);
return $this;
}
public function removePartLot(PartLot $lot): Part
{
$this->partLots->removeElement($lot);
return $this;
}
/**
* Returns the assigned manufacturer product number (MPN) for this part.
* @return string
*/
public function getManufacturerProductNumber(): string
{
return $this->manufacturer_product_number;
}
/**
* Sets the manufacturer product number (MPN) for this part.
* @param string $manufacturer_product_number
* @return Part
*/
public function setManufacturerProductNumber(string $manufacturer_product_number): Part
{
$this->manufacturer_product_number = $manufacturer_product_number;
return $this;
}
/**
* Gets the measurement unit in which the part's amount should be measured.
* Returns null if no specific unit was that. That means the parts are measured simply in quantity numbers.
* @return MeasurementUnit|null
*/
public function getPartUnit(): ?MeasurementUnit
{
return $this->partUnit;
}
/**
* Sets the measurement unit in which the part's amount should be measured.
* Set to null, if the part should be measured in quantities.
* @param MeasurementUnit|null $partUnit
* @return Part
*/
public function setPartUnit(?MeasurementUnit $partUnit): Part
{
$this->partUnit = $partUnit;
return $this;
}
/**
* Gets a comma separated list, of tags, that are assigned to this part
* @return string
*/
public function getTags(): string
{
return $this->tags;
}
/**
* Sets a comma separated list of tags, that are assigned to this part.
* @param string $tags
* @return Part
*/
public function setTags(string $tags): Part
{
$this->tags = $tags;
return $this;
}
/**
* Returns the mass of a single part unit.
* Returns null, if the mass is unknown/not set yet
* @return float|null
*/
public function getMass(): ?float
{
return $this->mass;
}
/**
* Sets the mass of a single part unit.
* Sett to null, if the mass is unknown.
* @param float|null $mass
* @return Part
*/
public function setMass(?float $mass): Part
{
$this->mass = $mass;
return $this;
}
/**
* Checks if this part uses the float amount .
* This setting is based on the part unit (see MeasurementUnit->isInteger()).
* @return bool True if the float amount field should be used. False if the integer instock field should be used.
*/
public function useFloatAmount(): bool
{
if ($this->partUnit instanceof MeasurementUnit) {
return !$this->partUnit->isInteger();
}
//When no part unit is set, treat it as part count, and so use the integer value.
return false;
}
/**
* Returns the summed amount of this part (over all part lots)
* @return float
*/
public function getAmountSum() : float
{
//TODO: Find a method to do this natively in SQL, the current method could be a bit slow
$sum = 0;
foreach ($this->getPartLots() as $lot) {
//Dont use the instock value, if it is unkown
if ($lot->isInstockUnknown()) {
continue;
}
$sum += $lot->getAmount();
}
if ($this->useFloatAmount()) {
return $sum;
}
return round($sum);
}
/********************************************************************************
*
* Setters
*
*********************************************************************************/
/**
* Set the description.
*
* @param string $new_description the new description
*
* @return self
*/
public function setDescription(?string $new_description): self
{
$this->description = $new_description;
return $this;
}
/**
* Set the minimum amount of parts that have to be instock.
* See getPartUnit() for the associated unit.
*
* @param int $new_minamount the new count of parts which should be in stock at least
*
* @return self
*/
public function setMinAmount(float $new_minamount): self
{
//Assert::natural($new_mininstock, 'The new minimum instock value must be positive! Got %s.');
$this->minamount = $new_minamount;
return $this;
}
/**
* Set the comment.
*
* @param string $new_comment the new comment
*
* @return self
*/
public function setComment(string $new_comment): self
{
$this->comment = $new_comment;
return $this;
}
/**
* Set the "manual_order" attribute.
*
* @param bool $new_manual_order the new "manual_order" attribute
* @param int $new_order_quantity the new order quantity
* @param int|null $new_order_orderdetails_id * the ID of the new order orderdetails
* * or Zero for "no order orderdetails"
* * or NULL for automatic order orderdetails
* (if the part has exactly one orderdetails,
* set this orderdetails as order orderdetails.
* Otherwise, set "no order orderdetails")
*
* @return self
*/
public function setManualOrder(bool $new_manual_order, int $new_order_quantity = 1, ?Orderdetail $new_order_orderdetail = null): Part
{
//Assert::greaterThan($new_order_quantity, 0, 'The new order quantity must be greater zero. Got %s!');
$this->manual_order = $new_manual_order;
//TODO;
$this->order_orderdetail = $new_order_orderdetail;
$this->order_quantity = $new_order_quantity;
return $this;
}
/**
* Set the category of this Part.
*
* Every part must have a valid category (in contrast to the
* attributes "footprint", "storelocation", ...)!
*
* @param Category $category The new category of this part
*
* @return self
*/
public function setCategory(Category $category): Part
{
$this->category = $category;
return $this;
}
/**
* Set the new Footprint of this Part.
*
* @param Footprint|null $new_footprint The new footprint of this part. Set to null, if this part should not have
* a footprint.
*
* @return self
*/
public function setFootprint(?Footprint $new_footprint): Part
{
$this->footprint = $new_footprint;
return $this;
}
/**
* Sets the new manufacturer of this part.
*
* @param Manufacturer|null $new_manufacturer The new Manufacturer of this part. Set to null, if this part should
* not have a manufacturer.
*
* @return Part
*/
public function setManufacturer(?Manufacturer $new_manufacturer): self
{
$this->manufacturer = $new_manufacturer;
return $this;
}
/**
* Set the favorite status for this part.
*
* @param $new_favorite_status bool The new favorite status, that should be applied on this part.
* Set this to true, when the part should be a favorite.
*
* @return self
*/
public function setFavorite(bool $new_favorite_status): self
{
$this->favorite = $new_favorite_status;
return $this;
}
/**
* Sets the URL to the manufacturer site about this Part. Set to "" if this part should use the automatically URL based on its manufacturer.
*
* @param string $new_url The new url
*
* @return self
*/
public function setManufacturerProductURL(string $new_url): self
{
$this->manufacturer_product_url = $new_url;
return $this;
}
}