<?php
namespace MedBrief\MSR\Entity;
use DH\Auditor\Provider\Doctrine\Auditing\Annotation as Audit;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use MedBrief\MSR\Entity\Embeddable\BillingItemPrice;
use MedBrief\MSR\Traits\VatAwareTrait;
/**
* Represents an InvoiceItem.
*
* An InvoiceItem is associated with an Invoice and a ServiceRequest. It also maintains a link
* to the BillingItem associated with it.
*
* @ORM\Table(name="InvoiceItem")
*
* @ORM\Entity
*
* @Audit\Auditable
*
* @Audit\Security(view={"ROLE_ALLOWED_TO_AUDIT"})
*/
class InvoiceItem
{
// Allows us to do VAT calculations
use VatAwareTrait;
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
*
* @ORM\Id
*
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* @var float
*
* @ORM\Column(name="quantity", type="decimal", precision=7, scale=2)
*/
protected $quantity;
/**
* @var \DateTime
*
* @ORM\Column(name="created", type="datetime")
*
* @Gedmo\Timestampable(on="create")
*/
protected $created;
/**
* @var \DateTime
*
* @ORM\Column(name="updated", type="datetime")
*
* @Gedmo\Timestampable(on="update")
*/
protected $updated;
/**
* @var BillingItemPrice
*
* @ORM\Embedded(class="MedBrief\MSR\Entity\Embeddable\BillingItemPrice", columnPrefix="price_")
*/
protected $price;
/**
* @var BillingItem
*
* @ORM\ManyToOne(targetEntity="MedBrief\MSR\Entity\BillingItem")
*
* @ORM\JoinColumns({
*
* @ORM\JoinColumn(name="billingItem_id", referencedColumnName="id")
* })
*/
protected $billingItem;
/**
* @var Invoice
*
* @ORM\ManyToOne(targetEntity="MedBrief\MSR\Entity\Invoice", inversedBy="items")
*
* @ORM\JoinColumns({
*
* @ORM\JoinColumn(name="invoice_id", referencedColumnName="id")
* })
*/
protected $invoice;
/**
* @var ServiceRequest
*
* @ORM\ManyToOne(targetEntity="MedBrief\MSR\Entity\ServiceRequest", inversedBy="invoiceItems")
*
* @ORM\JoinColumns({
*
* @ORM\JoinColumn(name="serviceRequest_id", referencedColumnName="id")
* })
*/
protected $serviceRequest;
/**
* Constructor
*/
public function __construct()
{
// Initialise the BillingItemPrice embeddable
$this->price = new BillingItemPrice();
}
/**
* __toString
*
* @return string
*/
public function __toString()
{
return (string) $this->getBillingItem()->getDescription() . ' - ' . $this->getQuantity() . ' x ' . $this->getPrice()->getUnitDescription();
}
/**
* Get id
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Set created
*
* @param \DateTime $created
*
* @return InvoiceItem
*/
public function setCreated($created)
{
$this->created = $created;
return $this;
}
/**
* Get created
*
* @return \DateTime
*/
public function getCreated()
{
return $this->created;
}
/**
* Set updated
*
* @param \DateTime $updated
*
* @return InvoiceItem
*/
public function setUpdated($updated)
{
$this->updated = $updated;
return $this;
}
/**
* Get updated
*
* @return \DateTime
*/
public function getUpdated()
{
return $this->updated;
}
/**
* Set price
*
* @param BillingItemPrice $price
*
* @return InvoiceItem
*/
public function setPrice(BillingItemPrice $price)
{
$this->price = $price;
return $this;
}
/**
* Get price
*
* @return BillingItemPrice
*/
public function getPrice()
{
return $this->price;
}
/**
* Checks if a price has been explicitly set for this InvoiceItem.
*
* Returns true if a price amount is present, regardless of its value (including zero).
* Returns false if the price amount is null or undefined.
*
* @return bool
*/
public function isPriceSet()
{
return $this->getPrice()->getAmount() !== null;
}
/**
* Set invoice
*
* @param Invoice $invoice
*
* @return InvoiceItem
*/
public function setInvoice(?Invoice $invoice = null)
{
$this->invoice = $invoice;
return $this;
}
/**
* Get invoice
*
* @return Invoice
*/
public function getInvoice()
{
return $this->invoice;
}
/**
* Set billingItem
*
* @param BillingItem $billingItem
*
* @return InvoiceItem
*/
public function setBillingItem(?BillingItem $billingItem = null)
{
$this->billingItem = $billingItem;
return $this;
}
/**
* Get billingItem
*
* @return BillingItem
*/
public function getBillingItem()
{
return $this->billingItem;
}
/**
* Set serviceRequest
*
* @param ServiceRequest $serviceRequest
*
* @return InvoiceItem
*/
public function setServiceRequest(?ServiceRequest $serviceRequest = null)
{
$this->serviceRequest = $serviceRequest;
return $this;
}
/**
* Get serviceRequest
*
* @return ServiceRequest
*/
public function getServiceRequest()
{
return $this->serviceRequest;
}
/**
* Set quantity
*
* @param int $quantity
*
* @return InvoiceItem
*/
public function setQuantity($quantity)
{
$this->quantity = $quantity;
return $this;
}
/**
* Get quantity
*
* @return int
*/
public function getQuantity()
{
return $this->quantity;
}
/**
* Calculates the total of the InvoiceItem.
*
* This function takes into account if the BillingItemPrice
* embeddable is indicated to be VAT inclusive, and adjusts the
* calculation accordingly.
*
* @throws \Exception
*
* @return float
*/
public function getTotal()
{
// Is the price VAT inclusive? Then use the calculated amount as the total.
if ($this->getPrice()->getVatInclusive()) {
return $this->calculateAmount();
}
// Else, the total is the VAT + the total excl VAT.
return $this->getTotalExclVat() + $this->getVat();
}
/**
* Calculates the vat of the InvoiceItem.
*
* This function takes into account if the BillingItemPrice
* embeddable is indicated to be VAT inclusive, and adjusts the
* calculation accordingly.
*
* This function uses the calculateVAT function of the VatAwareTrait.
*
* @throws \Exception
*
* @return float
*/
public function getVat()
{
// Is the price VAT inclusive?
if ($this->getPrice()->getVatInclusive()) {
// Calculate the VAT using the total, with the
// inclusive flag set. This extracts the VAT portion from the total.
return $this->calculateVat($this->getTotal(), true);
}
// Else, run the vat calculation as per normal.
return $this->calculateVat($this->getTotalExclVat());
}
/**
* Calculates the total excl. vat of the InvoiceItem.
*
* This function takes into account if the BillingItemPrice
* embeddable is indicated to be VAT inclusive, and adjusts the
* calculation accordingly.
*
* This function uses the calculateVAT function of the VatAwareTrait.
*
* @throws \Exception
*
* @return float
*/
public function getTotalExclVat()
{
// Is the price VAT inclusive?
if ($this->getPrice()->getVatInclusive()) {
// Then the total excl. VAT is the total less the VAT.
return $this->getTotal() - $this->getVat();
}
// Else, it is the calculated amount.
return $this->calculateAmount();
}
/**
* Calculates the Cost Per Unit excl VAT, by dividing the
* totalExclVat / quantity.
*
* @throws \Exception
*
* @return float
*/
public function getCostPerUnitExclVat()
{
return $this->getTotalExclVat() / $this->getQuantity();
}
/**
* Populates the price object of the InvoiceItem with the appropriate value, based
* on if Account level pricing exists for the Account associated with the InvoiceItem.
*
* @return InvoiceItem
*/
public function applyPrice()
{
$accountPrice = null;
// Ensure a ServiceRequest and BillingItem are attached to this InvoiceItem,
// and only bother with this logic if the BillingItem actually has Account Level Prices
if ($this->getServiceRequest() && $this->getBillingItem() && $this->getBillingItem()->getAccountPrices()->count()) {
$account = $this->getServiceRequest()->getServiceRequestGroup()->getProject()->getAccount();
// Filter out the account level price that matches the Account for the InvoiceItem
$accountPrice = $this->getBillingItem()->getAccountPrices()->filter(function ($accountPrice) use ($account) {
return $accountPrice->getAccount()->getId() === $account->getId();
});
}
// If an account level price was found...
if ($accountPrice && $accountPrice->count()) {
// apply it to the InvoiceItem by cloning it.
$this->setPrice(clone $accountPrice->first()->getPrice());
} else {
// else, we apply the BillingItem price, by cloning it.
$this->setPrice(clone $this->getBillingItem()->getPrice());
}
return $this;
}
/**
* Calulates the amount represented by the InvoiceItem,
* which is always the quantity multipled by the price->amount().
*
* This function should not be accessbile publicly.
*
* @return float
*/
protected function calculateAmount()
{
return $this->getPrice()->getAmount() * $this->getQuantity();
}
}