api/src/Payment/Entity/CarpoolItem.php
<?php
/**
* Copyright (c) 2020, MOBICOOP. All rights reserved.
* This project is dual licensed under AGPL and proprietary licence.
***************************
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <gnu.org/licenses>.
***************************
* Licence MOBICOOP described in the file
* LICENSE
*/
namespace App\Payment\Entity;
use App\Action\Entity\Log;
use App\Carpool\Entity\Ask;
use App\Carpool\Entity\CarpoolProof;
use App\Carpool\Entity\Proposal;
use App\Incentive\Validator\CarpoolPaymentValidator;
use App\User\Entity\User;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Serializer\Annotation\MaxDepth;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Carpool item : a carpool journey effectively done, or supposed to be done.
* The item must be kept even if related entities are deleted (eg. a user deletes its account, the carpooler must keep the item).
*
* @ORM\Entity
*
* @ORM\HasLifecycleCallbacks
*/
class CarpoolItem
{
public const STATUS_INITIALIZED = 0; // carpool supposed to be done
public const STATUS_REALIZED = 1; // carpool confirmed
public const STATUS_NOT_REALIZED = 2; // carpool invalidated (no carpool for this day)
public const DEBTOR_STATUS_NULL = -1; // no payment fo this item
public const DEBTOR_STATUS_PENDING = 0; // debtor has to pay
public const DEBTOR_STATUS_PENDING_ONLINE = 1; // debtor is paying online
public const DEBTOR_STATUS_PENDING_DIRECT = 2; // when debtor waits for creditor to confirm direct payment
public const DEBTOR_STATUS_ONLINE = 3; // debtor has paid online
public const DEBTOR_STATUS_DIRECT = 4; // debtor has paid manually (and creditor has confirmed)
public const CREDITOR_STATUS_NULL = -1; // no payment fo this item
public const CREDITOR_STATUS_PENDING = 0; // creditor has to confirm direct payment by the debtor
public const CREDITOR_STATUS_PENDING_ONLINE = 1; // credit is waiting for electronic payment
public const CREDITOR_STATUS_ONLINE = 3; // creditor was paid electronically
public const CREDITOR_STATUS_DIRECT = 4; // creditor has confirmed direct payment
// const CREDITOR_STATUS_UNPAID = 3;
/**
* @var int the id of this item
*
* @ORM\Id
*
* @ORM\GeneratedValue
*
* @ORM\Column(type="integer")
*
* @Groups({"readExport", "readSubscription"})
*
* @MaxDepth(1)
*/
private $id;
/**
* @var int Item type related with the ask :
* 1 : one way trip
* 2 : outward of a round trip
* 3 : return of a round trip
*
* @Assert\NotBlank
*
* @ORM\Column(type="smallint")
*/
private $type;
/**
* @var int The status of the carpool :
* 0 : the carpool was planned, we don't know yet if it has been realized
* 1 : the carpool has been realized (planned or dynamic)
* 2 : the carpool was planned but was not realized
*
* @Assert\NotBlank
*
* @ORM\Column(type="smallint")
*/
private $itemStatus;
/**
* @var \DateTimeInterface the date of the carpool (=date of the start of the carpool)
*
* @Assert\NotBlank
*
* @ORM\Column(type="date")
*
* @Groups({"readExport"})
*/
private $itemDate;
/**
* @var float the amount to be paid
*
* @Assert\NotBlank
*
* @ORM\Column(type="decimal", precision=6, scale=2)
*
* @Groups({"readExport"})
*/
private $amount;
/**
* @var int Debtor payment status :
* 0 : waiting for payment
* 1 : payment pending electronically
* 2 : payment pending manually
* 3 : payment done electronically
* 4 : payment done manually
*
* @ORM\Column(type="smallint")
*
* @Groups({"readExport"})
*/
private $debtorStatus;
/**
* @var int Creditor payment status :
* 0 : waiting for payment
* 1 : payment pending electronically
* 3 : payment received electronically
* 4 : payment received manually
*
* @ORM\Column(type="smallint")
*
* @Groups({"readExport"})
*/
private $creditorStatus;
/**
* @var Ask the ask related to the item
*
* @ORM\ManyToOne(targetEntity="\App\Carpool\Entity\Ask", inversedBy="carpoolItems")
*
* @ORM\JoinColumn(onDelete="SET NULL")
*
* @Groups({"readExport"})
*
* @MaxDepth(1)
*/
private $ask;
/**
* @var User Debtor user (the user that pays)
*
* @ORM\ManyToOne(targetEntity="\App\User\Entity\User")
*
* @ORM\JoinColumn(onDelete="SET NULL")
*
* @Groups({"readExport"})
*
* @MaxDepth(1)
*/
private $debtorUser;
/**
* @var User Creditor user (the user paid)
*
* @ORM\ManyToOne(targetEntity="\App\User\Entity\User")
*
* @ORM\JoinColumn(onDelete="SET NULL")
*
* @Groups({"readExport"})
*
* @MaxDepth(1)
*/
private $creditorUser;
/**
* @var null|ArrayCollection Payment tries for carpool items : many tries can be necessary for a successful payment. A payment may concern many items.
*
* @ORM\ManyToMany(targetEntity="\App\Payment\Entity\CarpoolPayment", mappedBy="carpoolItems")
*
* @MaxDepth(1)
*/
private $carpoolPayments;
/**
* @var ArrayCollection the logs linked with the carpoolitem
*
* @ORM\OneToMany(targetEntity="\App\Action\Entity\Log", mappedBy="carpoolItem")
*/
private $logs;
/**
* @var int Debtor Consumption feedback reponse code of the external service
* ONLY If this carpool item has been involved in a Consumption feedback
*
* @ORM\Column(type="integer", nullable=true)
*
* @MaxDepth(1)
*/
private $debtorConsumptionFeedbackReturnCode;
/**
* @var string Debtor Consumption feedback external id that has been sent to the service
* ONLY If this carpool item has been involved in a Consumption feedback
*
* @ORM\Column(type="string", length=255, nullable=true)
*
* @MaxDepth(1)
*/
private $debtorConsumptionFeedbackExternalId;
/**
* @var \DateTimeInterface Last try on sending a Debtor consumption feedback
* ONLY If this carpool item has been involved in a Consumption feedback
*
* @ORM\Column(type="datetime", nullable=true)
*
* @MaxDepth(1)
*/
private $debtorConsumptionFeedbackDate;
/**
* @var int Creditor Consumption feedback reponse code of the external service
* ONLY If this carpool item has been involved in a Consumption feedback
*
* @ORM\Column(type="integer", nullable=true)
*
* @MaxDepth(1)
*/
private $creditorConsumptionFeedbackReturnCode;
/**
* @var string Creditor Consumption feedback external id that has been sent to the service
* ONLY If this carpool item has been involved in a Consumption feedback
*
* @ORM\Column(type="string", length=255, nullable=true)
*
* @MaxDepth(1)
*/
private $creditorConsumptionFeedbackExternalId;
/**
* @var \DateTimeInterface Last try on sending a Creditor consumption feedback
* ONLY If this carpool item has been involved in a Consumption feedback
*
* @ORM\Column(type="datetime", nullable=true)
*
* @MaxDepth(1)
*/
private $creditorConsumptionFeedbackDate;
/**
* @var \DateTimeInterface creation date
*
* @ORM\Column(type="datetime")
*/
private $createdDate;
/**
* @var \DateTimeInterface updated date
*
* @ORM\Column(type="datetime", nullable=true)
*/
private $updatedDate;
/**
* @var \DateTimeInterface unpaid notify date
*
* @ORM\Column(type="datetime", nullable=true)
*
* @Groups({"readExport"})
*/
private $unpaidDate;
/**
* @var null|string pickup of the carpool
*
* @ORM\Column(type="string", length=255, nullable=true)
*
* @MaxDepth(1)
*/
private $pickUp;
/**
* @var null|string dropOff of the carpool
*
* @ORM\Column(type="string", length=255, nullable=true)
*
* @MaxDepth(1)
*/
private $dropOff;
/**
* @var null|int distance of the carpool
*
* @ORM\Column(type="integer", nullable=true)
*
* @MaxDepth(1)
*/
private $distance;
public function __construct()
{
$this->carpoolPayments = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getType(): ?int
{
return $this->type;
}
public function setType(?int $type): self
{
$this->type = $type;
return $this;
}
public function getItemStatus(): ?int
{
return $this->itemStatus;
}
public function setItemStatus(int $itemStatus): self
{
$this->itemStatus = $itemStatus;
return $this;
}
public function getAmount(): ?string
{
return $this->amount;
}
public function setAmount(?string $amount)
{
$this->amount = $amount;
}
public function getDebtorStatus(): ?int
{
return $this->debtorStatus;
}
public function setDebtorStatus(int $debtorStatus): self
{
$this->debtorStatus = $debtorStatus;
return $this;
}
public function getCreditorStatus(): ?int
{
return $this->creditorStatus;
}
public function setCreditorStatus(int $creditorStatus): self
{
$this->creditorStatus = $creditorStatus;
return $this;
}
public function getItemDate(): ?\DateTimeInterface
{
return $this->itemDate;
}
public function setItemDate(\DateTimeInterface $itemDate): self
{
$this->itemDate = $itemDate;
return $this;
}
public function getAsk(): ?Ask
{
return $this->ask;
}
public function setAsk(?Ask $ask): self
{
$this->ask = $ask;
return $this;
}
public function getDebtorUser(): ?User
{
return $this->debtorUser;
}
public function setDebtorUser(?User $debtorUser): self
{
$this->debtorUser = $debtorUser;
return $this;
}
public function getCreditorUser(): ?User
{
return $this->creditorUser;
}
public function setCreditorUser(?User $creditorUser): self
{
$this->creditorUser = $creditorUser;
return $this;
}
public function getCarpoolPayments()
{
return $this->carpoolPayments->getValues();
}
public function addCarpoolPayment(CarpoolPayment $carpoolPayment): self
{
if (!$this->carpoolPayments->contains($carpoolPayment)) {
$this->carpoolPayments[] = $carpoolPayment;
}
return $this;
}
public function removeCarpoolPayment(CarpoolPayment $carpoolPayment): self
{
if ($this->carpoolPayments->contains($carpoolPayment)) {
$this->carpoolPayments->removeElement($carpoolPayment);
}
return $this;
}
public function getCreatedDate(): ?\DateTimeInterface
{
return $this->createdDate;
}
public function setCreatedDate(\DateTimeInterface $createdDate): self
{
$this->createdDate = $createdDate;
return $this;
}
public function getUpdatedDate(): ?\DateTimeInterface
{
return $this->updatedDate;
}
public function setUpdatedDate(\DateTimeInterface $updatedDate): self
{
$this->updatedDate = $updatedDate;
return $this;
}
public function getUnpaidDate(): ?\DateTimeInterface
{
return $this->unpaidDate;
}
public function setUnpaidDate(?\DateTimeInterface $unpaidDate): self
{
$this->unpaidDate = $unpaidDate;
return $this;
}
public function getLogs()
{
return $this->logs->getValues();
}
public function addLog(Log $log): self
{
if (!$this->logs->contains($log)) {
$this->logs[] = $log;
$log->setCarpoolItem($this);
}
return $this;
}
public function removeLog(Log $log): self
{
if ($this->logs->contains($log)) {
$this->logs->removeElement($log);
// set the owning side to null (unless already changed)
if ($log->getCarpoolItem() === $this) {
$log->setCarpoolItem(null);
}
}
return $this;
}
public function getDebtorConsumptionFeedbackReturnCode(): ?int
{
return $this->debtorConsumptionFeedbackReturnCode;
}
public function setDebtorConsumptionFeedbackReturnCode(?int $debtorConsumptionFeedbackReturnCode): self
{
$this->debtorConsumptionFeedbackReturnCode = $debtorConsumptionFeedbackReturnCode;
return $this;
}
public function getDebtorConsumptionFeedbackExternalId(): ?string
{
return $this->debtorConsumptionFeedbackExternalId;
}
public function setDebtorConsumptionFeedbackExternalId(?string $debtorConsumptionFeedbackExternalId): self
{
$this->debtorConsumptionFeedbackExternalId = $debtorConsumptionFeedbackExternalId;
return $this;
}
public function getDebtorConsumptionFeedbackDate(): ?\DateTimeInterface
{
return $this->debtorConsumptionFeedbackDate;
}
public function setDebtorConsumptionFeedbackDate(?\DateTimeInterface $debtorConsumptionFeedbackDate): self
{
$this->debtorConsumptionFeedbackDate = $debtorConsumptionFeedbackDate;
return $this;
}
public function getCreditorConsumptionFeedbackReturnCode(): ?int
{
return $this->creditorConsumptionFeedbackReturnCode;
}
public function setCreditorConsumptionFeedbackReturnCode(?int $creditorConsumptionFeedbackReturnCode): self
{
$this->creditorConsumptionFeedbackReturnCode = $creditorConsumptionFeedbackReturnCode;
return $this;
}
public function getCreditorConsumptionFeedbackExternalId(): ?string
{
return $this->creditorConsumptionFeedbackExternalId;
}
public function setCreditorConsumptionFeedbackExternalId(?string $creditorConsumptionFeedbackExternalId): self
{
$this->creditorConsumptionFeedbackExternalId = $creditorConsumptionFeedbackExternalId;
return $this;
}
public function getCreditorConsumptionFeedbackDate(): ?\DateTimeInterface
{
return $this->creditorConsumptionFeedbackDate;
}
public function setCreditorConsumptionFeedbackDate(?\DateTimeInterface $creditorConsumptionFeedbackDate): self
{
$this->creditorConsumptionFeedbackDate = $creditorConsumptionFeedbackDate;
return $this;
}
public function getPickUp(): ?string
{
return $this->pickUp;
}
public function setPickUp(?string $pickUp): self
{
$this->pickUp = $pickUp;
return $this;
}
public function getDropOff(): ?string
{
return $this->dropOff;
}
public function setDropOff(?string $dropOff): self
{
$this->dropOff = $dropOff;
return $this;
}
public function getDistance(): ?int
{
return $this->distance;
}
public function setDistance(?int $distance): self
{
$this->distance = $distance;
return $this;
}
// DOCTRINE EVENTS
/**
* Item status.
*
* @ORM\PrePersist
*/
public function setAutoItemStatus()
{
$this->setItemStatus(self::STATUS_INITIALIZED);
}
/**
* Debtor status.
*
* @ORM\PrePersist
*/
public function setAutoDebtorStatus()
{
if (is_null($this->getDebtorStatus())) {
$this->setDebtorStatus(self::DEBTOR_STATUS_PENDING);
}
}
/**
* Creditor status.
*
* @ORM\PrePersist
*/
public function setAutoCreditorStatus()
{
if (is_null($this->getCreditorStatus())) {
$this->setCreditorStatus(self::CREDITOR_STATUS_PENDING);
}
}
/**
* Creation date.
*
* @ORM\PrePersist
*/
public function setAutoCreatedDate()
{
$this->setCreatedDate(new \DateTime());
}
/**
* Update date.
*
* @ORM\PreUpdate
*/
public function setAutoUpdatedDate()
{
$this->setUpdatedDate(new \DateTime());
}
public function getProposalAccordingUser(User $user): ?Proposal
{
return
!is_null($this->getAsk())
&& !is_null($this->getAsk()->getMatching())
&& !is_null($this->getAsk()->getMatching()->getProposalOffer())
&& !is_null($this->getAsk()->getMatching()->getProposalOffer()->getUser())
&& $user->getId() === $this->getAsk()->getMatching()->getProposalOffer()->getUser()->getId()
? $this->getAsk()->getMatching()->getProposalOffer()->getUser()
: (
!is_null($this->getAsk())
&& !is_null($this->getAsk()->getMatching())
&& !is_null($this->getAsk()->getMatching()->getProposalRequest())
&& !is_null($this->getAsk()->getMatching()->getProposalRequest()->getUser())
? $this->getAsk()->getMatching()->getProposalRequest()->getUser()
: null
);
}
public function getCarpoolProof(): ?CarpoolProof
{
if (
is_null($this->getAsk())
|| is_null($this->getAsk()->getCarpoolProofs())
|| empty($this->getAsk()->getCarpoolProofs())
) {
return null;
}
$filteredCarpoolProofs = array_values(array_filter(
$this->getAsk()->getCarpoolProofs(),
function ($carpoolProof) {
$referenceDate = !is_null($carpoolProof->getPickUpDriverDate())
? $carpoolProof->getPickUpDriverDate() // Returns the driver pickup date
: (
!is_null($carpoolProof->getPickUpPassengerDate())
? $carpoolProof->getPickUpPassengerDate() // Returns the passenger pickup date
: $carpoolProof->getCreatedDate()->sub(new \DateInterval('P1D')) // Returns the day before the proof creation date
);
return $this->getItemDate()->format('Y-m-d') === $referenceDate->format('Y-m-d');
}
));
return !empty($filteredCarpoolProofs) ? $filteredCarpoolProofs[0] : null;
}
/**
* Used in the context of EEC, return the associated payment. This latest must meet the criteria:
* - Have been successfully paid,
* - Keep track of the transaction.
*/
public function getSuccessfullPayment(): ?CarpoolPayment
{
$successFullPayment = array_values(array_filter($this->getCarpoolPayments(), function (CarpoolPayment $carpoolPayment) {
return CarpoolPaymentValidator::isEecCompliant($carpoolPayment);
}));
return !empty($successFullPayment) ? $successFullPayment[0] : null;
}
/**
* Used in the context of EEC, returns the distance of the carpoolitem and if it is zero, that of the corresponding matching.
*/
public function getRelativeDistance(): ?int
{
return
!is_null($this->getDistance())
? $this->getDistance()
: (
!is_null($this->getAsk())
&& !is_null($this->getAsk()->getMatching())
? $this->getAsk()->getMatching()->getCommonDistance()
: null
);
}
/**
* Used in the EEC context, returns if the carpool item and its associated CarpoolPaiement is EEC Compliant.
*/
public function isEECompliant(): bool
{
return
self::CREDITOR_STATUS_ONLINE === $this->getCreditorStatus()
&& !is_null($this->getSuccessfullPayment());
}
}