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.commission": "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. /**
  291. * @Groups({"list"})
  292. * @SerializedName("total")
  293. * @return int|null
  294. */
  295. public function getTotal(): ?int
  296. {
  297. return $this->total;
  298. }
  299. /**
  300. * Calculate the total from positions
  301. *
  302. * @return int
  303. */
  304. public function calculateTotal(): int
  305. {
  306. $calculatedTotal = 0;
  307. foreach ($this->positions as $position) {
  308. if ($position->getState() == 'draft') {
  309. continue;
  310. }
  311. $calculatedTotal += $position->getTotalWithTax();
  312. }
  313. return $calculatedTotal;
  314. }
  315. /**
  316. * Set the total value
  317. *
  318. * @param int|null $total
  319. * @return self
  320. */
  321. public function setTotal(?int $total): self
  322. {
  323. $this->total = $total;
  324. return $this;
  325. }
  326. /**
  327. * @ORM\PrePersist
  328. * @ORM\PreUpdate
  329. */
  330. public function updateTotal(): void
  331. {
  332. $this->total = $this->calculateTotal();
  333. }
  334. public function getArticleCount()
  335. {
  336. $total = 0;
  337. /** @var OrderPosition $position */
  338. foreach ($this->positions as $position) {
  339. if ($position->getState() == 'added') {
  340. foreach ($position->getArticles() as $article) {
  341. $total += $article['amount'];
  342. }
  343. }
  344. }
  345. return $total;
  346. }
  347. public function newValue(AttributeInterface $attribute): AttributeValueInterface
  348. {
  349. return new OrderAttributeValue($attribute);
  350. }
  351. /**
  352. * @Groups({"list"})
  353. * @SerializedName("tax")
  354. * @return string|null
  355. */
  356. public function getTax()
  357. {
  358. if(!$this->isApplyMwst()){
  359. return 1;
  360. }
  361. /** @var EstimateAttribute $tax */
  362. $taxAttribute = $this->getAttribute('tax');
  363. if(!$taxAttribute){
  364. return 1;
  365. }
  366. /** @var EstimateAttributeValue $value */
  367. $value = $this->getAttributeValue($taxAttribute);
  368. $tax = $value->getValue();
  369. if(!is_float($tax)){
  370. $tax = floatval($tax);
  371. }
  372. return (100 + $tax) / 100;
  373. }
  374. public function getTotalPriceBrutto()
  375. {
  376. return $total / 100 * (100 + $tax);
  377. }
  378. public function setAttributeValues(Collection $attributeValues): AttributeValueAwareInterface
  379. {
  380. $this->attributeValues = $attributeValues;
  381. return $this;
  382. }
  383. /**
  384. * @return Collection|EstimateAttributeValue []
  385. */
  386. public function getAttributeValues(): Collection
  387. {
  388. return $this->attributeValues;
  389. }
  390. public function getHighlightedDiscounts() {
  391. $discounts = [];
  392. /** @var EstimatePosition $position */
  393. foreach($this->positions as $position) {
  394. foreach($position->getDiscounts() as $discount) {
  395. if (isset($discount['highlight']) && $discount['highlight'] == true) {
  396. if (!isset($discounts[$discount['description']])) {
  397. $discounts[$discount['description']] = $discount;
  398. } else {
  399. $discounts[$discount['description']]['total'] += $discount['total'];
  400. }
  401. }
  402. }
  403. }
  404. return $discounts;
  405. }
  406. public function isAllPositionsValid(){
  407. return $this->getPositions()->filter(function($position){
  408. return $position->isPositionInvalid();
  409. })->count() === 0;
  410. }
  411. /**
  412. * @return mixed
  413. */
  414. public function getType()
  415. {
  416. return $this->type;
  417. }
  418. /**
  419. * @param mixed $type
  420. */
  421. public function setType($type): void
  422. {
  423. $this->type = $type;
  424. }
  425. /**
  426. * @return Estimate
  427. */
  428. public function getEstimate()
  429. {
  430. return $this->estimate;
  431. }
  432. /**
  433. * @param Estimate $estimate
  434. */
  435. public function setEstimate($estimate)
  436. {
  437. $this->estimate = $estimate;
  438. return $this;
  439. }
  440. }