vendor/roothirsch/shop-bundle/Entity/Estimate.php line 82

Open in your IDE?
  1. <?php
  2. namespace Roothirsch\ShopBundle\Entity;
  3. use ApiPlatform\Core\Annotation\ApiResource;
  4. use ApiPlatform\Core\Annotation\ApiFilter;
  5. use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\OrderFilter;
  6. use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\SearchFilter;
  7. use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\BooleanFilter;
  8. use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\DateFilter;
  9. use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\ExistsFilter;
  10. use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\NumericFilter;
  11. use Roothirsch\CoreBundle\Behaviors\Attributable\AttributeValue\AttributeValueAwareInterface;
  12. use Roothirsch\CoreBundle\Behaviors\Attributable\AttributeValue\AttributeValueAwareTrait;
  13. use Roothirsch\CoreBundle\Behaviors\Attributable\AttributeValue\AttributeValueInterface;
  14. use Roothirsch\CoreBundle\Behaviors\Attributable\Attribute\AttributeInterface;
  15. use Roothirsch\CoreBundle\Behaviors\Attributable\Filter\AttributableSearchFilter;
  16. use Roothirsch\CoreBundle\Behaviors\Attributable\MappedSuperclass\AbstractAttributable;
  17. use Roothirsch\CoreBundle\Entity\Traits\TimetrackedTrait;
  18. use Roothirsch\CoreBundle\UserAware\UserAwareInterface;
  19. use Roothirsch\CoreBundle\UserAware\UserAwareTrait;
  20. use Doctrine\Common\Collections\ArrayCollection;
  21. use Doctrine\Common\Collections\Collection;
  22. use Doctrine\ORM\Mapping as ORM;
  23. use Roothirsch\ShopBundle\API\Filter\HasPositionsFilter;
  24. use Roothirsch\ShopBundle\Entity\Estimate\EstimateAttributeValue;
  25. use Roothirsch\ShopBundle\Entity\Estimate\EstimateAttribute;
  26. use Symfony\Component\Serializer\Annotation\Groups;
  27. /**
  28. * @ORM\Entity(repositoryClass="Roothirsch\ShopBundle\Repository\EstimateRepository")
  29. * @ORM\Table(name="shop_estimate")
  30. * @ORM\HasLifecycleCallbacks()
  31. * @ApiResource(
  32. * shortName="Shop/Estimate",
  33. * normalizationContext={"groups"={"list"}, "enable_max_depth"=true},
  34. * denormalizationContext={"groups"={"list"}},
  35. * itemOperations={
  36. * "order"={"method"="PUT", "path"="/estimates/{id}/order", "requirements"={"id"=".+"}},
  37. * "get",
  38. * "put",
  39. * "delete"
  40. * },
  41. * collectionOperations={
  42. * "duplicate"={"method"="POST", "path"="/estimates/{id}/duplicate", "requirements"={"id"=".+"}},
  43. * "get",
  44. * "post"
  45. * }
  46. * )
  47. * @ApiFilter(Roothirsch\ShopBundle\API\Filter\AttributeOrderFilter::class, properties={
  48. * "createdAt",
  49. * "totalPrice",
  50. * "total",
  51. * "name",
  52. * "comment",
  53. * "id",
  54. * "applyMwst",
  55. * "attributes.reference",
  56. * "attributes.customer",
  57. * "attributes.address",
  58. * "attributes.contactPerson",
  59. * "attributes.comission",
  60. * }, arguments={"orderParameterName"="order"})
  61. * @ApiFilter(SearchFilter::class, properties={
  62. * "id": "exact",
  63. * "name": "partial",
  64. * "comment": "partial",
  65. * "archived": "exact",
  66. * })
  67. * @ApiFilter(NumericFilter::class, properties={"totalPrice", "total"})
  68. * @ApiFilter(AttributableSearchFilter::class, properties={
  69. * "attributes.reference": "partial",
  70. * "attributes.customer": "partial",
  71. * "attributes.comission": "partial",
  72. * })
  73. * @ApiFilter(NumericFilter::class, properties={"totalPrice", "total"})
  74. * @ApiFilter(BooleanFilter::class, properties={"archived"})
  75. * @ApiFilter(DateFilter::class, properties={"createdAt"})
  76. * @ApiFilter(HasPositionsFilter::class)
  77. */
  78. class Estimate extends AbstractAttributable implements UserAwareInterface
  79. {
  80. use UserAwareTrait;
  81. use TimetrackedTrait;
  82. /**
  83. * @ORM\Id
  84. * @ORM\GeneratedValue
  85. * @ORM\Column(type="integer")
  86. * @Groups({"list"})
  87. */
  88. private $id;
  89. /**
  90. * @ORM\Column(type="string", length=255, nullable=true)
  91. * @Groups({"list"})
  92. */
  93. private $name;
  94. /**
  95. * @ORM\OneToMany(targetEntity=EstimatePosition::class, mappedBy="estimate", orphanRemoval=true, cascade={"persist"})
  96. * @Groups({"list"})
  97. */
  98. private $positions;
  99. /**
  100. * @ORM\Column(type="boolean")
  101. * @Groups({"list"})
  102. */
  103. private $applyDiscount = true;
  104. /**
  105. * @ORM\Column(type="boolean")
  106. * @Groups({"list"})
  107. */
  108. private $applyMwst = false;
  109. /**
  110. * @ORM\Column(type="string", nullable=true)
  111. * @Groups({"list"})
  112. */
  113. private $comment;
  114. /**
  115. * @ORM\Column(type="float", nullable=true)
  116. * @Groups({"list"})
  117. */
  118. private $totalPrice;
  119. /**
  120. * @ORM\Column(type="integer", nullable=true)
  121. * @Groups({"list"})
  122. */
  123. private $total;
  124. /**
  125. * @ORM\OneToMany(targetEntity=EstimateAttributeValue::class, mappedBy="estimate", orphanRemoval=true, cascade={"persist"})
  126. */
  127. private $attributeValues;
  128. /**
  129. * @ORM\Column(type="boolean", options={"default": 0})
  130. * @Groups({"list"})
  131. */
  132. private $archived = false;
  133. public function __construct()
  134. {
  135. $this->positions = new ArrayCollection();
  136. $this->attributeValues = new ArrayCollection();
  137. }
  138. public function __clone() {
  139. $this->id = null;
  140. $this->setCreatedAt(new \DateTime());
  141. $this->setUpdatedAt(new \DateTime());
  142. $oldPositions = $this->positions;
  143. $this->positions = new ArrayCollection();
  144. foreach($oldPositions as $oldPosition) {
  145. $this->addPosition(clone $oldPosition);
  146. }
  147. $oldAttributeValues = $this->attributeValues;
  148. $this->attributeValues = new ArrayCollection();
  149. foreach($oldAttributeValues as $oldAttributeValue) {
  150. $this->addAttributeValue(clone $oldAttributeValue);
  151. }
  152. }
  153. public function getAttributes()
  154. {
  155. return $this->attributes;
  156. }
  157. /**
  158. * @return mixed
  159. */
  160. public function getId()
  161. {
  162. return $this->id;
  163. }
  164. /**
  165. * @param mixed $id
  166. */
  167. public function setId($id)
  168. {
  169. $this->id = $id;
  170. }
  171. public function getName(): ?string
  172. {
  173. return $this->name;
  174. }
  175. public function setName(?string $name): self
  176. {
  177. $this->name = $name;
  178. return $this;
  179. }
  180. /**
  181. * @return Collection|EstimatePosition[]
  182. */
  183. public function getPositions(): Collection
  184. {
  185. return $this->positions;
  186. }
  187. public function addPosition(EstimatePosition $position): self
  188. {
  189. if (!$this->positions->contains($position)) {
  190. $this->positions[] = $position;
  191. $position->setEstimate($this);
  192. }
  193. return $this;
  194. }
  195. public function removePosition(EstimatePosition $position): self
  196. {
  197. if ($this->positions->removeElement($position)) {
  198. // set the owning side to null (unless already changed)
  199. if ($position->getEstimate() === $this) {
  200. $position->setEstimate(null);
  201. }
  202. }
  203. return $this;
  204. }
  205. /**
  206. * @return bool
  207. */
  208. public function isApplyDiscount(): bool
  209. {
  210. return $this->applyDiscount;
  211. }
  212. /**
  213. * @param bool $applyDiscount
  214. */
  215. public function setApplyDiscount(bool $applyDiscount): void
  216. {
  217. $this->applyDiscount = $applyDiscount;
  218. }
  219. /**
  220. * @return bool
  221. */
  222. public function isApplyMwst(): bool
  223. {
  224. return $this->applyMwst;
  225. }
  226. /**
  227. * @param bool $applyMwst
  228. */
  229. public function setApplyMwst(bool $applyMwst): void
  230. {
  231. $this->applyMwst = $applyMwst;
  232. }
  233. /**
  234. * @return mixed
  235. */
  236. public function getComment()
  237. {
  238. return $this->comment;
  239. }
  240. /**
  241. * @param mixed $comment
  242. */
  243. public function setComment($comment): void
  244. {
  245. $this->comment = $comment;
  246. }
  247. /**
  248. * @Groups({"list"})
  249. */
  250. public function getTotal(): ?int
  251. {
  252. return $this->total;
  253. }
  254. /**
  255. * Calculate the total from positions
  256. *
  257. * @return int
  258. */
  259. public function calculateTotal(): int
  260. {
  261. $calculatedTotal = 0;
  262. /** @var EstimatePosition $position */
  263. foreach ($this->positions as $position) {
  264. if ($position->getState() == 'draft') {
  265. continue;
  266. }
  267. if ($position->getOptional()) {
  268. continue;
  269. }
  270. $calculatedTotal += $position->getTotalWithTax();
  271. }
  272. return $calculatedTotal;
  273. }
  274. /**
  275. * Set the total value
  276. *
  277. * @param int|null $total
  278. * @return self
  279. */
  280. public function setTotal(?int $total): self
  281. {
  282. $this->total = $total;
  283. return $this;
  284. }
  285. /**
  286. * @ORM\PrePersist
  287. * @ORM\PreUpdate
  288. */
  289. public function updateTotal(): void
  290. {
  291. $this->total = $this->calculateTotal();
  292. }
  293. public function getTax()
  294. {
  295. if(!$this->isApplyMwst()){
  296. return 1;
  297. }
  298. /** @var EstimateAttribute $tax */
  299. $taxAttribute = $this->getAttribute('tax');
  300. if(!$taxAttribute){
  301. return 1;
  302. }
  303. /** @var EstimateAttributeValue $value */
  304. $value = $this->getAttributeValue($taxAttribute);
  305. $tax = $value->getValue();
  306. if(!is_float($tax)){
  307. $tax = floatval($tax);
  308. }
  309. return (100 + $tax) / 100;
  310. }
  311. public function getTotalPriceBrutto()
  312. {
  313. return $total / 100 * (100 + $tax);
  314. }
  315. public function getArticleCount()
  316. {
  317. $total = 0;
  318. /** @var OrderPosition $position */
  319. foreach ($this->positions as $position) {
  320. if ($position->getState() == 'added') {
  321. $total += count($position->getArticles());
  322. }
  323. }
  324. return $total;
  325. }
  326. /**
  327. * Get the value of totalPrice
  328. */
  329. public function getTotalPrice()
  330. {
  331. return $this->totalPrice;
  332. }
  333. /**
  334. * Set the value of totalPrice
  335. *
  336. * @return self
  337. */
  338. public function setTotalPrice($totalPrice)
  339. {
  340. $this->totalPrice = $totalPrice;
  341. return $this;
  342. }
  343. public function newValue(AttributeInterface $attribute): AttributeValueInterface
  344. {
  345. return new EstimateAttributeValue($attribute);
  346. }
  347. public function setAttributeValues(Collection $attributeValues): AttributeValueAwareInterface
  348. {
  349. $this->attributeValues = $attributeValues;
  350. return $this;
  351. }
  352. /**
  353. * @return Collection|EstimateAttributeValue []
  354. */
  355. public function getAttributeValues(): Collection
  356. {
  357. return $this->attributeValues;
  358. }
  359. public function getHighlightedDiscounts() {
  360. $discounts = [];
  361. /** @var EstimatePosition $position */
  362. foreach($this->positions as $position) {
  363. if ($position->getOptional()) {
  364. continue;
  365. }
  366. foreach($position->getDiscounts() as $discount) {
  367. if (isset($discount['highlight']) && $discount['highlight'] == true) {
  368. if (!isset($discounts[$discount['description']])) {
  369. $discounts[$discount['description']] = $discount;
  370. } else {
  371. $discounts[$discount['description']]['total'] += $discount['total'];
  372. }
  373. }
  374. }
  375. }
  376. return $discounts;
  377. }
  378. public function hasOptionalPositions() {
  379. foreach($this->positions as $position) {
  380. if ($position->getOptional()) {
  381. return true;
  382. }
  383. }
  384. return false;
  385. }
  386. /**
  387. * @return bool
  388. */
  389. public function isArchived()
  390. {
  391. return $this->archived;
  392. }
  393. /**
  394. * @param bool $archived
  395. */
  396. public function setArchived($archived)
  397. {
  398. $this->archived = $archived;
  399. }
  400. }