OpenStockOrder::class, 2 => PurchaseOrder::class, 3 => SalesOrder::class, ])] abstract class Orders { #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column] private ?int $id = null; #[ORM\Column(type: 'uuid', unique: true)] private ?Uuid $uuid = null; #[ORM\OneToMany(targetEntity: OrderItem::class, mappedBy: 'orders')] private Collection $orderLineItems; #[ORM\Column(length: 100, nullable: true)] private ?string $reference = null; #[ORM\ManyToOne(inversedBy: 'orders')] #[ORM\JoinColumn(nullable: false)] private ?Status $status = null; #[ORM\Column(type: Types::DATE_MUTABLE, nullable: true)] private ?\DateTimeInterface $cancel_date = null; #[ORM\OneToMany(targetEntity: Journal::class, mappedBy: 'order')] private ?Collection $journal; #[ORM\OneToMany(targetEntity: Payment::class, mappedBy: 'orders')] private Collection $payments_on_order; #[ORM\ManyToOne(inversedBy: 'orders')] #[ORM\JoinColumn(nullable: false)] private ?TransactionType $order_type = null; #[ORM\ManyToOne(inversedBy: 'orders')] private ?Location $location = null; #[ORM\OneToOne(mappedBy: 'orders', cascade: ['persist', 'remove'])] private ?OrderDetails $orderDetails = null; #[ORM\Column(nullable: true)] private ?bool $isPaid = null; #[ORM\OneToMany(targetEntity: OrderHistory::class, mappedBy: 'orders')] private Collection $orderHistories; #[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'parent')] private ?self $parent_order = null; #[ORM\OneToMany(targetEntity: self::class, mappedBy: 'parent_order')] private Collection $parent; #[ORM\ManyToOne(inversedBy: 'user_orders')] #[ORM\JoinColumn(nullable: true)] private ?Contact $billed_to = null; #[ORM\Column(type: Types::DECIMAL, precision: 20, scale: 2, nullable: true)] private ?string $delivery_amount = null; #[ORM\ManyToOne(inversedBy: 'account_orders')] #[ORM\JoinColumn(nullable: true)] private ?Account $billing_account = null; #[ORM\Column(type: Types::DECIMAL, precision: 20, scale: 2)] private ?string $subtotal = null; #[ORM\Column(type: Types::DECIMAL, precision: 20, scale: 2)] private ?string $total_discount = null; #[ORM\Column(type: Types::DECIMAL, precision: 20, scale: 2)] private ?string $vat_amount = null; #[ORM\Column(type: Types::DECIMAL, precision: 20, scale: 2)] private ?string $total = null; public function __construct() { $this->orderLineItems = new ArrayCollection(); $this->orderHistories = new ArrayCollection(); $this->journal = new ArrayCollection(); $this->parent = new ArrayCollection(); } public function getId(): ?int { return $this->id; } public function getReference(): ?string { return $this->reference; } public function setReference(string $reference): static { $this->reference = $reference; return $this; } /** * @return Collection */ public function getOrderLineItems(): Collection { return $this->orderLineItems; } public function addOrderLineItem(OrderItem $orderLineItem): static { if (!$this->orderLineItems->contains($orderLineItem)) { $this->orderLineItems->add($orderLineItem); $orderLineItem->setOrder($this); } return $this; } public function removeOrderLineItem(OrderItem $orderLineItem): static { if ($this->orderLineItems->removeElement($orderLineItem)) { // set the owning side to null (unless already changed) if ($orderLineItem->getOrder() === $this) { $orderLineItem->setOrder(null); } } return $this; } public function getStatus(): ?Status { return $this->status; } public function setStatus(?Status $status): static { $this->status = $status; return $this; } public function getCancelDate(): ?\DateTimeInterface { return $this->cancel_date; } public function setCancelDate(?\DateTimeInterface $cancel_date): static { $this->cancel_date = $cancel_date; return $this; } public function getCreatedBy(): ?Contact { return $this->createdBy; } public function setCreatedBy(?Contact $createdBy): static { $this->createdBy = $createdBy; return $this; } /** * @return Collection */ public function getJournal(): Collection { return $this->journal; } public function addJournal(Journal $journal): static { if (!$this->journal->contains($journal)) { $this->journal->add($journal); $journal->setOrder($this); } return $this; } public function removeJournal(Journal $journal): static { if ($this->journal->removeElement($journal)) { // set the owning side to null (unless already changed) if ($journal->getOrder() === $this) { $journal->setOrder(null); } } return $this; } /** * @return Collection */ public function getPaymentsOnOrder(): Collection { return $this->payments_on_order; } public function addPaymentsOnOrder(Payment $paymentsOnOrder): static { if (!$this->payments_on_order->contains($paymentsOnOrder)) { $this->payments_on_order->add($paymentsOnOrder); $paymentsOnOrder->setOrder($this); } return $this; } public function removePaymentsOnOrder(Payment $paymentsOnOrder): static { if ($this->payments_on_order->removeElement($paymentsOnOrder)) { // set the owning side to null (unless already changed) if ($paymentsOnOrder->getOrder() === $this) { $paymentsOnOrder->setOrder(null); } } return $this; } public function getOrderType(): ?TransactionType { return $this->order_type; } public function setOrderType(?TransactionType $order_type): static { $this->order_type = $order_type; // Validate that the order_type matches the DiscriminatorMap $expectedDiscriminator = $this->getDiscriminatorValue($order_type); if ($this->getOrderDiscriminatorId() !== $expectedDiscriminator) { throw new \LogicException(sprintf( 'Mismatch between order_type_id (%d) and order_discriminator_id (%d)', $order_type->getId(), $expectedDiscriminator )); } return $this; } public function getOrderDiscriminatorId(): ?int { // Doctrine manages this column internally, but we can access it indirectly $reflector = new \ReflectionClass($this); $discriminatorColumn = $reflector->getStaticPropertyValue('order_discriminator_id'); return $discriminatorColumn; } private function getDiscriminatorValue(TransactionType $TransactionType): int { $discriminatorMap = [ 1 => OpenStockOrder::class, 2 => SalesOrder::class, ]; foreach ($discriminatorMap as $id => $className) { if (get_class($this) === $className) { return $id; } } throw new \LogicException(sprintf( 'Unable to determine discriminator value for order type "%s".', $TransactionType->getName() )); } public function getLocation(): ?Location { return $this->location; } public function setLocation(?Location $location): static { $this->location = $location; return $this; } public function getOrderDetails(): ?OrderDetails { return $this->orderDetails; } public function setOrderDetails(OrderDetails $orderDetails): static { // set the owning side of the relation if necessary if ($orderDetails->getOrder() !== $this) { $orderDetails->setOrder($this); } $this->orderDetails = $orderDetails; return $this; } public function isIsPaid(): ?bool { return $this->isPaid; } public function setIsPaid(?bool $isPaid): static { $this->isPaid = $isPaid; return $this; } /** * @return Collection */ public function getOrderHistories(): Collection { return $this->orderHistories; } public function addOrderHistory(OrderHistory $orderHistory): static { if (!$this->orderHistories->contains($orderHistory)) { $this->orderHistories->add($orderHistory); $orderHistory->setOrders($this); } return $this; } public function removeOrderHistory(OrderHistory $orderHistory): static { if ($this->orderHistories->removeElement($orderHistory)) { // set the owning side to null (unless already changed) if ($orderHistory->getOrders() === $this) { $orderHistory->setOrders(null); } } return $this; } public function getParentOrder(): ?self { return $this->parent_order; } public function setParentOrder(?self $parent_order): static { $this->parent_order = $parent_order; return $this; } /** * @return Collection */ public function getParent(): Collection { return $this->parent; } public function addParent(self $parent): static { if (!$this->parent->contains($parent)) { $this->parent->add($parent); $parent->setParentOrder($this); } return $this; } public function removeParent(self $parent): static { if ($this->parent->removeElement($parent)) { // set the owning side to null (unless already changed) if ($parent->getParentOrder() === $this) { $parent->setParentOrder(null); } } return $this; } public function getBilledTo(): ?Contact { return $this->billed_to; } public function setBilledTo(?Contact $billed_to): static { $this->billed_to = $billed_to; return $this; } public function getDeliveryAmount(): ?string { return $this->delivery_amount; } public function setDeliveryAmount(?string $delivery_amount): static { $this->delivery_amount = $delivery_amount; return $this; } public function getBillingAccount(): ?Account { return $this->billing_account; } public function setBillingAccount(?Account $billing_account): static { $this->billing_account = $billing_account; return $this; } public function getSubtotal(): ?string { return $this->subtotal; } public function setSubtotal(string $subtotal): static { $this->subtotal = $subtotal; return $this; } public function getTotalDiscount(): ?string { return $this->total_discount; } public function setTotalDiscount(string $total_discount): static { $this->total_discount = $total_discount; return $this; } public function getVatAmount(): ?string { return $this->vat_amount; } public function setVatAmount(string $vat_amount): static { $this->vat_amount = $vat_amount; return $this; } public function getTotal(): ?string { return $this->total; } public function setTotal(string $total): static { $this->total = $total; return $this; } public function calculateTotal(): float { $total = 0; foreach ($this->getOrderLineItems() as $lineItem) { $quantity = $lineItem->getQty(); $price = $lineItem->getUnitPrice(); $discount = $lineItem->getDiscountAmount() ?? 0.00; $vat_amount = $lineItem->getVatAmount() ?? 0.00; $subTotal += $price * $quantity; $totalDiscount += $discount * $quantity; $netTotal += ($price - $discount) * $quantity; $vatAmount += $vat_amount * $quantity; $total += $netTotal + $vatAmount; } return $total; } // #[ArrayShape([ // 'dr' => "array{ledger: Ledger|null, amount: 'float'|'int'|null, hasInventoryMovement: 'bool', inventoryMovementIsDebit: 'bool', isReserveMovement: 'bool'}", // 'cr' => "array{ledger: Ledger|null, amount: 'float'|'int'|null, hasInventoryMovement: 'bool', inventoryMovementIsDebit: 'bool', isReserveMovement: 'bool'}", // ])] // abstract public function getJournalTypeConfig($trxData = []): array ; }