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. * Approval token for distributor approval workflow
  142. *
  143. * @ORM\Column(type="string", length=64, nullable=true)
  144. */
  145. private ?string $token = null;
  146. /**
  147. * Token expiration time
  148. *
  149. * @ORM\Column(type="datetime", nullable=true)
  150. */
  151. private ?\DateTimeInterface $tokenExpiresAt = null;
  152. /**
  153. * @ORM\OneToMany(targetEntity=OrderAttributeValue::class, mappedBy="order", orphanRemoval=true, cascade={"persist"})
  154. */
  155. private $attributeValues;
  156. /**
  157. * @ORM\Column(type="integer", nullable=true)
  158. * @Groups({"list"})
  159. */
  160. private $total;
  161. /**
  162. * @ORM\OneToOne(targetEntity=Estimate::class)
  163. * @ORM\JoinColumn(nullable=true)
  164. */
  165. private $estimate;
  166. public function __construct()
  167. {
  168. $this->positions = new ArrayCollection();
  169. $this->attributeValues = new ArrayCollection();
  170. }
  171. public function __clone() {
  172. $this->id = null;
  173. $this->setCreatedAt(new \DateTime());
  174. $this->setUpdatedAt(new \DateTime());
  175. $this->orderedAt = null;
  176. $oldPositions = $this->positions;
  177. $this->positions = new ArrayCollection();
  178. foreach($oldPositions as $oldPosition) {
  179. $this->addPosition(clone $oldPosition);
  180. }
  181. $oldAttributeValues = $this->attributeValues;
  182. $this->attributeValues = new ArrayCollection();
  183. foreach($oldAttributeValues as $oldAttributeValue) {
  184. $this->addAttributeValue(clone $oldAttributeValue);
  185. }
  186. }
  187. /**
  188. * @return mixed
  189. */
  190. public function getId()
  191. {
  192. return $this->id;
  193. }
  194. /**
  195. * @param mixed $id
  196. */
  197. public function setId($id)
  198. {
  199. $this->id = $id;
  200. }
  201. public function getName(): ?string
  202. {
  203. return $this->name;
  204. }
  205. public function setName(?string $name): self
  206. {
  207. $this->name = $name;
  208. return $this;
  209. }
  210. public function getAttributes()
  211. {
  212. return $this->attributes;
  213. }
  214. /**
  215. * @return Collection|OrderPosition[]
  216. */
  217. public function getPositions(): Collection
  218. {
  219. return $this->positions;
  220. }
  221. public function addPosition(OrderPosition $position): self
  222. {
  223. if (!$this->positions->contains($position)) {
  224. $this->positions[] = $position;
  225. $position->setOrder($this);
  226. }
  227. return $this;
  228. }
  229. public function removePosition(OrderPosition $position): self
  230. {
  231. if ($this->positions->removeElement($position)) {
  232. // set the owning side to null (unless already changed)
  233. if ($position->getOrder() === $this) {
  234. $position->setOrder(null);
  235. }
  236. }
  237. return $this;
  238. }
  239. /**
  240. * @return bool
  241. */
  242. public function isApplyDiscount(): bool
  243. {
  244. return $this->applyDiscount;
  245. }
  246. /**
  247. * @param bool $applyDiscount
  248. */
  249. public function setApplyDiscount(bool $applyDiscount): void
  250. {
  251. $this->applyDiscount = $applyDiscount;
  252. }
  253. /**
  254. * @return bool
  255. */
  256. public function isApplyMwst(): bool
  257. {
  258. return $this->applyMwst;
  259. }
  260. /**
  261. * @param bool $applyMwst
  262. */
  263. public function setApplyMwst(bool $applyMwst): void
  264. {
  265. $this->applyMwst = $applyMwst;
  266. }
  267. /**
  268. * @return mixed
  269. */
  270. public function getComment()
  271. {
  272. return $this->comment;
  273. }
  274. /**
  275. * @param mixed $comment
  276. */
  277. public function setComment($comment): void
  278. {
  279. $this->comment = $comment;
  280. }
  281. /**
  282. * @return \DateTime
  283. */
  284. public function getOrderedAt(): ?\DateTime
  285. {
  286. return $this->orderedAt;
  287. }
  288. /**
  289. * @param \DateTime $orderedAt
  290. */
  291. public function setOrderedAt(\DateTime $orderedAt): void
  292. {
  293. $this->orderedAt = $orderedAt;
  294. }
  295. /**
  296. * @return mixed
  297. */
  298. public function getState()
  299. {
  300. return $this->state;
  301. }
  302. public function setState(string $state): void
  303. {
  304. $this->state = $state;
  305. }
  306. /**
  307. * @Groups({"list"})
  308. * @SerializedName("total")
  309. * @return int|null
  310. */
  311. public function getTotal(): ?int
  312. {
  313. return $this->total;
  314. }
  315. /**
  316. * Calculate the total from positions
  317. *
  318. * @return int
  319. */
  320. public function calculateTotal(): int
  321. {
  322. $calculatedTotal = 0;
  323. foreach ($this->positions as $position) {
  324. if ($position->getState() == 'draft') {
  325. continue;
  326. }
  327. $calculatedTotal += $position->getTotalWithTax();
  328. }
  329. return $calculatedTotal;
  330. }
  331. /**
  332. * Set the total value
  333. *
  334. * @param int|null $total
  335. * @return self
  336. */
  337. public function setTotal(?int $total): self
  338. {
  339. $this->total = $total;
  340. return $this;
  341. }
  342. /**
  343. * @ORM\PrePersist
  344. * @ORM\PreUpdate
  345. */
  346. public function updateTotal(): void
  347. {
  348. $this->total = $this->calculateTotal();
  349. }
  350. public function getArticleCount()
  351. {
  352. $total = 0;
  353. /** @var OrderPosition $position */
  354. foreach ($this->positions as $position) {
  355. if ($position->getState() == 'added') {
  356. foreach ($position->getArticles() as $article) {
  357. $total += $article['amount'];
  358. }
  359. }
  360. }
  361. return $total;
  362. }
  363. public function newValue(AttributeInterface $attribute): AttributeValueInterface
  364. {
  365. return new OrderAttributeValue($attribute);
  366. }
  367. /**
  368. * @Groups({"list"})
  369. * @SerializedName("tax")
  370. * @return string|null
  371. */
  372. public function getTax()
  373. {
  374. if(!$this->isApplyMwst()){
  375. return 1;
  376. }
  377. /** @var EstimateAttribute $tax */
  378. $taxAttribute = $this->getAttribute('tax');
  379. if(!$taxAttribute){
  380. return 1;
  381. }
  382. /** @var EstimateAttributeValue $value */
  383. $value = $this->getAttributeValue($taxAttribute);
  384. $tax = $value->getValue();
  385. if(!is_float($tax)){
  386. $tax = floatval($tax);
  387. }
  388. return (100 + $tax) / 100;
  389. }
  390. public function getTotalPriceBrutto()
  391. {
  392. return $total / 100 * (100 + $tax);
  393. }
  394. public function setAttributeValues(Collection $attributeValues): AttributeValueAwareInterface
  395. {
  396. $this->attributeValues = $attributeValues;
  397. return $this;
  398. }
  399. /**
  400. * @return Collection|EstimateAttributeValue []
  401. */
  402. public function getAttributeValues(): Collection
  403. {
  404. return $this->attributeValues;
  405. }
  406. public function getHighlightedDiscounts() {
  407. $discounts = [];
  408. /** @var EstimatePosition $position */
  409. foreach($this->positions as $position) {
  410. foreach($position->getDiscounts() as $discount) {
  411. if (isset($discount['highlight']) && $discount['highlight'] == true) {
  412. if (!isset($discounts[$discount['description']])) {
  413. $discounts[$discount['description']] = $discount;
  414. } else {
  415. $discounts[$discount['description']]['total'] += $discount['total'];
  416. }
  417. }
  418. }
  419. }
  420. return $discounts;
  421. }
  422. public function isAllPositionsValid(){
  423. return $this->getPositions()->filter(function($position){
  424. return $position->isPositionInvalid();
  425. })->count() === 0;
  426. }
  427. /**
  428. * @return mixed
  429. */
  430. public function getType()
  431. {
  432. return $this->type;
  433. }
  434. /**
  435. * @param mixed $type
  436. */
  437. public function setType($type): void
  438. {
  439. $this->type = $type;
  440. }
  441. /**
  442. * @return Estimate
  443. */
  444. public function getEstimate()
  445. {
  446. return $this->estimate;
  447. }
  448. /**
  449. * @param Estimate $estimate
  450. */
  451. public function setEstimate($estimate)
  452. {
  453. $this->estimate = $estimate;
  454. return $this;
  455. }
  456. /**
  457. * Get approval token
  458. */
  459. public function getToken(): ?string
  460. {
  461. return $this->token;
  462. }
  463. /**
  464. * Set approval token
  465. */
  466. public function setToken(?string $token): self
  467. {
  468. $this->token = $token;
  469. return $this;
  470. }
  471. /**
  472. * Get token expiration time
  473. */
  474. public function getTokenExpiresAt(): ?\DateTimeInterface
  475. {
  476. return $this->tokenExpiresAt;
  477. }
  478. /**
  479. * Set token expiration time
  480. */
  481. public function setTokenExpiresAt(?\DateTimeInterface $tokenExpiresAt): self
  482. {
  483. $this->tokenExpiresAt = $tokenExpiresAt;
  484. return $this;
  485. }
  486. }