vendor/roothirsch/shop-bundle/Entity/Order.php line 91

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