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

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(AttributableSearchFilter::class, properties={
  68. * "attributes.reference": "exact",
  69. * "attributes.customer": "exact",
  70. * "attributes.comission": "exact",
  71. * "attributes.reference": "partial",
  72. * "attributes.customer": "partial",
  73. * "attributes.address": "partial",
  74. * "attributes.contactPerson": "partial",
  75. * "attributes.comission": "partial",
  76. * "attributes.tax": "exact"
  77. * })
  78. * @ApiFilter(AttributableSearchFilter::class, properties={
  79. * "attributes.reference": "exact",
  80. * "attributes.customer": "exact",
  81. * })
  82. * @ApiFilter(NumericFilter::class, properties={"totalPrice", "total"})
  83. * @ApiFilter(NumericFilter::class, properties={"totalPrice"})
  84. * @ApiFilter(BooleanFilter::class, properties={"archived"})
  85. * @ApiFilter(DateFilter::class, properties={"createdAt"})
  86. * @ApiFilter(HasPositionsFilter::class)
  87. */
  88. class Estimate extends AbstractAttributable implements UserAwareInterface
  89. {
  90. use UserAwareTrait;
  91. use TimetrackedTrait;
  92. /**
  93. * @ORM\Id
  94. * @ORM\GeneratedValue
  95. * @ORM\Column(type="integer")
  96. * @Groups({"list"})
  97. */
  98. private $id;
  99. /**
  100. * @ORM\Column(type="string", length=255, nullable=true)
  101. * @Groups({"list"})
  102. */
  103. private $name;
  104. /**
  105. * @ORM\OneToMany(targetEntity=EstimatePosition::class, mappedBy="estimate", orphanRemoval=true, cascade={"persist"})
  106. * @Groups({"list"})
  107. */
  108. private $positions;
  109. /**
  110. * @ORM\Column(type="boolean")
  111. * @Groups({"list"})
  112. */
  113. private $applyDiscount = true;
  114. /**
  115. * @ORM\Column(type="boolean")
  116. * @Groups({"list"})
  117. */
  118. private $applyMwst = false;
  119. /**
  120. * @ORM\Column(type="string", nullable=true)
  121. * @Groups({"list"})
  122. */
  123. private $comment;
  124. /**
  125. * @ORM\Column(type="float", nullable=true)
  126. * @Groups({"list"})
  127. */
  128. private $totalPrice;
  129. /**
  130. * @ORM\Column(type="integer", nullable=true)
  131. * @Groups({"list"})
  132. */
  133. private $total;
  134. /**
  135. * @ORM\OneToMany(targetEntity=EstimateAttributeValue::class, mappedBy="estimate", orphanRemoval=true, cascade={"persist"})
  136. */
  137. private $attributeValues;
  138. /**
  139. * @ORM\Column(type="boolean", options={"default": 0})
  140. * @Groups({"list"})
  141. */
  142. private $archived = false;
  143. public function __construct()
  144. {
  145. $this->positions = new ArrayCollection();
  146. $this->attributeValues = new ArrayCollection();
  147. }
  148. public function __clone() {
  149. $this->id = null;
  150. $this->setCreatedAt(new \DateTime());
  151. $this->setUpdatedAt(new \DateTime());
  152. $oldPositions = $this->positions;
  153. $this->positions = new ArrayCollection();
  154. foreach($oldPositions as $oldPosition) {
  155. $this->addPosition(clone $oldPosition);
  156. }
  157. $oldAttributeValues = $this->attributeValues;
  158. $this->attributeValues = new ArrayCollection();
  159. foreach($oldAttributeValues as $oldAttributeValue) {
  160. $this->addAttributeValue(clone $oldAttributeValue);
  161. }
  162. }
  163. public function getAttributes()
  164. {
  165. return $this->attributes;
  166. }
  167. /**
  168. * @return mixed
  169. */
  170. public function getId()
  171. {
  172. return $this->id;
  173. }
  174. /**
  175. * @param mixed $id
  176. */
  177. public function setId($id)
  178. {
  179. $this->id = $id;
  180. }
  181. public function getName(): ?string
  182. {
  183. return $this->name;
  184. }
  185. public function setName(?string $name): self
  186. {
  187. $this->name = $name;
  188. return $this;
  189. }
  190. /**
  191. * @return Collection|EstimatePosition[]
  192. */
  193. public function getPositions(): Collection
  194. {
  195. return $this->positions;
  196. }
  197. public function addPosition(EstimatePosition $position): self
  198. {
  199. if (!$this->positions->contains($position)) {
  200. $this->positions[] = $position;
  201. $position->setEstimate($this);
  202. }
  203. return $this;
  204. }
  205. public function removePosition(EstimatePosition $position): self
  206. {
  207. if ($this->positions->removeElement($position)) {
  208. // set the owning side to null (unless already changed)
  209. if ($position->getEstimate() === $this) {
  210. $position->setEstimate(null);
  211. }
  212. }
  213. return $this;
  214. }
  215. /**
  216. * @return bool
  217. */
  218. public function isApplyDiscount(): bool
  219. {
  220. return $this->applyDiscount;
  221. }
  222. /**
  223. * @param bool $applyDiscount
  224. */
  225. public function setApplyDiscount(bool $applyDiscount): void
  226. {
  227. $this->applyDiscount = $applyDiscount;
  228. }
  229. /**
  230. * @return bool
  231. */
  232. public function isApplyMwst(): bool
  233. {
  234. return $this->applyMwst;
  235. }
  236. /**
  237. * @param bool $applyMwst
  238. */
  239. public function setApplyMwst(bool $applyMwst): void
  240. {
  241. $this->applyMwst = $applyMwst;
  242. }
  243. /**
  244. * @return mixed
  245. */
  246. public function getComment()
  247. {
  248. return $this->comment;
  249. }
  250. /**
  251. * @param mixed $comment
  252. */
  253. public function setComment($comment): void
  254. {
  255. $this->comment = $comment;
  256. }
  257. /**
  258. * @Groups({"list"})
  259. */
  260. public function getTotal(): ?int
  261. {
  262. return $this->total;
  263. }
  264. /**
  265. * Calculate the total from positions
  266. *
  267. * @return int
  268. */
  269. public function calculateTotal(): int
  270. {
  271. $calculatedTotal = 0;
  272. /** @var EstimatePosition $position */
  273. foreach ($this->positions as $position) {
  274. if ($position->getState() == 'draft') {
  275. continue;
  276. }
  277. if ($position->getOptional()) {
  278. continue;
  279. }
  280. $calculatedTotal += $position->getTotalWithTax();
  281. }
  282. return $calculatedTotal;
  283. }
  284. /**
  285. * Set the total value
  286. *
  287. * @param int|null $total
  288. * @return self
  289. */
  290. public function setTotal(?int $total): self
  291. {
  292. $this->total = $total;
  293. return $this;
  294. }
  295. /**
  296. * @ORM\PrePersist
  297. * @ORM\PreUpdate
  298. */
  299. public function updateTotal(): void
  300. {
  301. $this->total = $this->calculateTotal();
  302. }
  303. public function getTax()
  304. {
  305. if(!$this->isApplyMwst()){
  306. return 1;
  307. }
  308. /** @var EstimateAttribute $tax */
  309. $taxAttribute = $this->getAttribute('tax');
  310. if(!$taxAttribute){
  311. return 1;
  312. }
  313. /** @var EstimateAttributeValue $value */
  314. $value = $this->getAttributeValue($taxAttribute);
  315. $tax = $value->getValue();
  316. if(!is_float($tax)){
  317. $tax = floatval($tax);
  318. }
  319. return (100 + $tax) / 100;
  320. }
  321. public function getTotalPriceBrutto()
  322. {
  323. return $total / 100 * (100 + $tax);
  324. }
  325. public function getArticleCount()
  326. {
  327. $total = 0;
  328. /** @var OrderPosition $position */
  329. foreach ($this->positions as $position) {
  330. if ($position->getState() == 'added') {
  331. $total += count($position->getArticles());
  332. }
  333. }
  334. return $total;
  335. }
  336. /**
  337. * Get the value of totalPrice
  338. */
  339. public function getTotalPrice()
  340. {
  341. return $this->totalPrice;
  342. }
  343. /**
  344. * Set the value of totalPrice
  345. *
  346. * @return self
  347. */
  348. public function setTotalPrice($totalPrice)
  349. {
  350. $this->totalPrice = $totalPrice;
  351. return $this;
  352. }
  353. public function newValue(AttributeInterface $attribute): AttributeValueInterface
  354. {
  355. return new EstimateAttributeValue($attribute);
  356. }
  357. public function setAttributeValues(Collection $attributeValues): AttributeValueAwareInterface
  358. {
  359. $this->attributeValues = $attributeValues;
  360. return $this;
  361. }
  362. /**
  363. * @return Collection|EstimateAttributeValue []
  364. */
  365. public function getAttributeValues(): Collection
  366. {
  367. return $this->attributeValues;
  368. }
  369. public function getHighlightedDiscounts() {
  370. $discounts = [];
  371. /** @var EstimatePosition $position */
  372. foreach($this->positions as $position) {
  373. if ($position->getOptional()) {
  374. continue;
  375. }
  376. foreach($position->getDiscounts() as $discount) {
  377. if (isset($discount['highlight']) && $discount['highlight'] == true) {
  378. if (!isset($discounts[$discount['description']])) {
  379. $discounts[$discount['description']] = $discount;
  380. } else {
  381. $discounts[$discount['description']]['total'] += $discount['total'];
  382. }
  383. }
  384. }
  385. }
  386. return $discounts;
  387. }
  388. public function hasOptionalPositions() {
  389. foreach($this->positions as $position) {
  390. if ($position->getOptional()) {
  391. return true;
  392. }
  393. }
  394. return false;
  395. }
  396. /**
  397. * @return bool
  398. */
  399. public function isArchived()
  400. {
  401. return $this->archived;
  402. }
  403. /**
  404. * @param bool $archived
  405. */
  406. public function setArchived($archived)
  407. {
  408. $this->archived = $archived;
  409. }
  410. }