src/Entity/InvoiceItem.php line 25

Open in your IDE?
  1. <?php
  2. namespace MedBrief\MSR\Entity;
  3. use DH\Auditor\Provider\Doctrine\Auditing\Annotation as Audit;
  4. use Doctrine\ORM\Mapping as ORM;
  5. use Gedmo\Mapping\Annotation as Gedmo;
  6. use MedBrief\MSR\Entity\Embeddable\BillingItemPrice;
  7. use MedBrief\MSR\Traits\VatAwareTrait;
  8. /**
  9. * Represents an InvoiceItem.
  10. *
  11. * An InvoiceItem is associated with an Invoice and a ServiceRequest. It also maintains a link
  12. * to the BillingItem associated with it.
  13. *
  14. * @ORM\Table(name="InvoiceItem")
  15. *
  16. * @ORM\Entity
  17. *
  18. * @Audit\Auditable
  19. *
  20. * @Audit\Security(view={"ROLE_ALLOWED_TO_AUDIT"})
  21. */
  22. class InvoiceItem
  23. {
  24. // Allows us to do VAT calculations
  25. use VatAwareTrait;
  26. /**
  27. * @var int
  28. *
  29. * @ORM\Column(name="id", type="integer")
  30. *
  31. * @ORM\Id
  32. *
  33. * @ORM\GeneratedValue(strategy="IDENTITY")
  34. */
  35. protected $id;
  36. /**
  37. * @var float
  38. *
  39. * @ORM\Column(name="quantity", type="decimal", precision=7, scale=2)
  40. */
  41. protected $quantity;
  42. /**
  43. * @var \DateTime
  44. *
  45. * @ORM\Column(name="created", type="datetime")
  46. *
  47. * @Gedmo\Timestampable(on="create")
  48. */
  49. protected $created;
  50. /**
  51. * @var \DateTime
  52. *
  53. * @ORM\Column(name="updated", type="datetime")
  54. *
  55. * @Gedmo\Timestampable(on="update")
  56. */
  57. protected $updated;
  58. /**
  59. * @var BillingItemPrice
  60. *
  61. * @ORM\Embedded(class="MedBrief\MSR\Entity\Embeddable\BillingItemPrice", columnPrefix="price_")
  62. */
  63. protected $price;
  64. /**
  65. * @var BillingItem
  66. *
  67. * @ORM\ManyToOne(targetEntity="MedBrief\MSR\Entity\BillingItem")
  68. *
  69. * @ORM\JoinColumns({
  70. *
  71. * @ORM\JoinColumn(name="billingItem_id", referencedColumnName="id")
  72. * })
  73. */
  74. protected $billingItem;
  75. /**
  76. * @var Invoice
  77. *
  78. * @ORM\ManyToOne(targetEntity="MedBrief\MSR\Entity\Invoice", inversedBy="items")
  79. *
  80. * @ORM\JoinColumns({
  81. *
  82. * @ORM\JoinColumn(name="invoice_id", referencedColumnName="id")
  83. * })
  84. */
  85. protected $invoice;
  86. /**
  87. * @var ServiceRequest
  88. *
  89. * @ORM\ManyToOne(targetEntity="MedBrief\MSR\Entity\ServiceRequest", inversedBy="invoiceItems")
  90. *
  91. * @ORM\JoinColumns({
  92. *
  93. * @ORM\JoinColumn(name="serviceRequest_id", referencedColumnName="id")
  94. * })
  95. */
  96. protected $serviceRequest;
  97. /**
  98. * Constructor
  99. */
  100. public function __construct()
  101. {
  102. // Initialise the BillingItemPrice embeddable
  103. $this->price = new BillingItemPrice();
  104. }
  105. /**
  106. * __toString
  107. *
  108. * @return string
  109. */
  110. public function __toString()
  111. {
  112. return (string) $this->getBillingItem()->getDescription() . ' - ' . $this->getQuantity() . ' x ' . $this->getPrice()->getUnitDescription();
  113. }
  114. /**
  115. * Get id
  116. *
  117. * @return int
  118. */
  119. public function getId()
  120. {
  121. return $this->id;
  122. }
  123. /**
  124. * Set created
  125. *
  126. * @param \DateTime $created
  127. *
  128. * @return InvoiceItem
  129. */
  130. public function setCreated($created)
  131. {
  132. $this->created = $created;
  133. return $this;
  134. }
  135. /**
  136. * Get created
  137. *
  138. * @return \DateTime
  139. */
  140. public function getCreated()
  141. {
  142. return $this->created;
  143. }
  144. /**
  145. * Set updated
  146. *
  147. * @param \DateTime $updated
  148. *
  149. * @return InvoiceItem
  150. */
  151. public function setUpdated($updated)
  152. {
  153. $this->updated = $updated;
  154. return $this;
  155. }
  156. /**
  157. * Get updated
  158. *
  159. * @return \DateTime
  160. */
  161. public function getUpdated()
  162. {
  163. return $this->updated;
  164. }
  165. /**
  166. * Set price
  167. *
  168. * @param BillingItemPrice $price
  169. *
  170. * @return InvoiceItem
  171. */
  172. public function setPrice(BillingItemPrice $price)
  173. {
  174. $this->price = $price;
  175. return $this;
  176. }
  177. /**
  178. * Get price
  179. *
  180. * @return BillingItemPrice
  181. */
  182. public function getPrice()
  183. {
  184. return $this->price;
  185. }
  186. /**
  187. * Checks if a price has been explicitly set for this InvoiceItem.
  188. *
  189. * Returns true if a price amount is present, regardless of its value (including zero).
  190. * Returns false if the price amount is null or undefined.
  191. *
  192. * @return bool
  193. */
  194. public function isPriceSet()
  195. {
  196. return $this->getPrice()->getAmount() !== null;
  197. }
  198. /**
  199. * Set invoice
  200. *
  201. * @param Invoice $invoice
  202. *
  203. * @return InvoiceItem
  204. */
  205. public function setInvoice(?Invoice $invoice = null)
  206. {
  207. $this->invoice = $invoice;
  208. return $this;
  209. }
  210. /**
  211. * Get invoice
  212. *
  213. * @return Invoice
  214. */
  215. public function getInvoice()
  216. {
  217. return $this->invoice;
  218. }
  219. /**
  220. * Set billingItem
  221. *
  222. * @param BillingItem $billingItem
  223. *
  224. * @return InvoiceItem
  225. */
  226. public function setBillingItem(?BillingItem $billingItem = null)
  227. {
  228. $this->billingItem = $billingItem;
  229. return $this;
  230. }
  231. /**
  232. * Get billingItem
  233. *
  234. * @return BillingItem
  235. */
  236. public function getBillingItem()
  237. {
  238. return $this->billingItem;
  239. }
  240. /**
  241. * Set serviceRequest
  242. *
  243. * @param ServiceRequest $serviceRequest
  244. *
  245. * @return InvoiceItem
  246. */
  247. public function setServiceRequest(?ServiceRequest $serviceRequest = null)
  248. {
  249. $this->serviceRequest = $serviceRequest;
  250. return $this;
  251. }
  252. /**
  253. * Get serviceRequest
  254. *
  255. * @return ServiceRequest
  256. */
  257. public function getServiceRequest()
  258. {
  259. return $this->serviceRequest;
  260. }
  261. /**
  262. * Set quantity
  263. *
  264. * @param int $quantity
  265. *
  266. * @return InvoiceItem
  267. */
  268. public function setQuantity($quantity)
  269. {
  270. $this->quantity = $quantity;
  271. return $this;
  272. }
  273. /**
  274. * Get quantity
  275. *
  276. * @return int
  277. */
  278. public function getQuantity()
  279. {
  280. return $this->quantity;
  281. }
  282. /**
  283. * Calculates the total of the InvoiceItem.
  284. *
  285. * This function takes into account if the BillingItemPrice
  286. * embeddable is indicated to be VAT inclusive, and adjusts the
  287. * calculation accordingly.
  288. *
  289. * @throws \Exception
  290. *
  291. * @return float
  292. */
  293. public function getTotal()
  294. {
  295. // Is the price VAT inclusive? Then use the calculated amount as the total.
  296. if ($this->getPrice()->getVatInclusive()) {
  297. return $this->calculateAmount();
  298. }
  299. // Else, the total is the VAT + the total excl VAT.
  300. return $this->getTotalExclVat() + $this->getVat();
  301. }
  302. /**
  303. * Calculates the vat of the InvoiceItem.
  304. *
  305. * This function takes into account if the BillingItemPrice
  306. * embeddable is indicated to be VAT inclusive, and adjusts the
  307. * calculation accordingly.
  308. *
  309. * This function uses the calculateVAT function of the VatAwareTrait.
  310. *
  311. * @throws \Exception
  312. *
  313. * @return float
  314. */
  315. public function getVat()
  316. {
  317. // Is the price VAT inclusive?
  318. if ($this->getPrice()->getVatInclusive()) {
  319. // Calculate the VAT using the total, with the
  320. // inclusive flag set. This extracts the VAT portion from the total.
  321. return $this->calculateVat($this->getTotal(), true);
  322. }
  323. // Else, run the vat calculation as per normal.
  324. return $this->calculateVat($this->getTotalExclVat());
  325. }
  326. /**
  327. * Calculates the total excl. vat of the InvoiceItem.
  328. *
  329. * This function takes into account if the BillingItemPrice
  330. * embeddable is indicated to be VAT inclusive, and adjusts the
  331. * calculation accordingly.
  332. *
  333. * This function uses the calculateVAT function of the VatAwareTrait.
  334. *
  335. * @throws \Exception
  336. *
  337. * @return float
  338. */
  339. public function getTotalExclVat()
  340. {
  341. // Is the price VAT inclusive?
  342. if ($this->getPrice()->getVatInclusive()) {
  343. // Then the total excl. VAT is the total less the VAT.
  344. return $this->getTotal() - $this->getVat();
  345. }
  346. // Else, it is the calculated amount.
  347. return $this->calculateAmount();
  348. }
  349. /**
  350. * Calculates the Cost Per Unit excl VAT, by dividing the
  351. * totalExclVat / quantity.
  352. *
  353. * @throws \Exception
  354. *
  355. * @return float
  356. */
  357. public function getCostPerUnitExclVat()
  358. {
  359. return $this->getTotalExclVat() / $this->getQuantity();
  360. }
  361. /**
  362. * Populates the price object of the InvoiceItem with the appropriate value, based
  363. * on if Account level pricing exists for the Account associated with the InvoiceItem.
  364. *
  365. * @return InvoiceItem
  366. */
  367. public function applyPrice()
  368. {
  369. $accountPrice = null;
  370. // Ensure a ServiceRequest and BillingItem are attached to this InvoiceItem,
  371. // and only bother with this logic if the BillingItem actually has Account Level Prices
  372. if ($this->getServiceRequest() && $this->getBillingItem() && $this->getBillingItem()->getAccountPrices()->count()) {
  373. $account = $this->getServiceRequest()->getServiceRequestGroup()->getProject()->getAccount();
  374. // Filter out the account level price that matches the Account for the InvoiceItem
  375. $accountPrice = $this->getBillingItem()->getAccountPrices()->filter(function ($accountPrice) use ($account) {
  376. return $accountPrice->getAccount()->getId() === $account->getId();
  377. });
  378. }
  379. // If an account level price was found...
  380. if ($accountPrice && $accountPrice->count()) {
  381. // apply it to the InvoiceItem by cloning it.
  382. $this->setPrice(clone $accountPrice->first()->getPrice());
  383. } else {
  384. // else, we apply the BillingItem price, by cloning it.
  385. $this->setPrice(clone $this->getBillingItem()->getPrice());
  386. }
  387. return $this;
  388. }
  389. /**
  390. * Calulates the amount represented by the InvoiceItem,
  391. * which is always the quantity multipled by the price->amount().
  392. *
  393. * This function should not be accessbile publicly.
  394. *
  395. * @return float
  396. */
  397. protected function calculateAmount()
  398. {
  399. return $this->getPrice()->getAmount() * $this->getQuantity();
  400. }
  401. }