<?php
// src/Service/UserService.php
namespace App\Service;
use App\Entity\Donation;
use App\Entity\FilmProject;
use App\Entity\OrderDetails;
use App\Entity\RecurringDonation;
use App\Entity\User;
use App\Repository\ProductRepository;
use App\Service\Xero\ContactService;
use App\Service\Xero\InvoiceService;
use App\String\Constant;
use App\Utility\PdfExporter;
use Doctrine\ORM\EntityManagerInterface;
use Knp\Snappy\Pdf;
use Twig\Environment;
class DonationService
{
private OrderDetailsService $orderDetailsService;
private SalesforceService $salesforceService;
private EntityManagerInterface $em;
private MerchantWarriorService $mwService;
private ContactService $xeroContactService;
private InvoiceService $xeroInvoiceService;
private MailService $mailService;
private UserService $userService;
private SettingService $settingService;
private WordpressService $wpService;
private Environment $twig;
private Pdf $pdf;
public function __construct(OrderDetailsService $orderDetailsService, SalesforceService $salesforceService, EntityManagerInterface $em, MerchantWarriorService $mwService, ContactService $xeroContactService, InvoiceService $xeroInvoiceService, MailService $mailService, UserService $userService, SettingService $settingService, WordpressService $wpService, Environment $twig, Pdf $pdf)
{
$this->orderDetailsService = $orderDetailsService;
$this->salesforceService = $salesforceService;
$this->em = $em;
$this->xeroContactService = $xeroContactService;
$this->xeroInvoiceService = $xeroInvoiceService;
$this->mwService = $mwService;
$this->mailService = $mailService;
$this->userService = $userService;
$this->settingService = $settingService;
$this->wpService = $wpService;
$this->twig = $twig;
$this->pdf = $pdf;
}
/**
* Create donation object
*
* @param User $user
* @return Donation
*/
public function createObject(?User $user, FilmProject $filmProject = null, $amount = null, array $recurringDetails = null, $supportdaf = 0)
{
$currentDatetime = new \DateTime('now');
$orderDetails = $this->orderDetailsService->createDonationOrderObj($user, $supportdaf);
$donation = (new Donation())
->setCreatedAt($currentDatetime)
->setModifiedAt($currentDatetime)
->setOrderDetails($orderDetails)
;
if ($filmProject) {
$donation->setFilmProject($filmProject);
$orderItems = $orderDetails->getOrderItems();
$orderItems[0]->setProduct($filmProject->getProduct());
if ($supportdaf > 0) {
$dafProduct = $this->settingService->getDefaultDafDonationProduct();
$orderItems[1]->setProduct($dafProduct);
$donation->setSupportdaf($supportdaf);
$amount = $amount + $supportdaf;
}
// $orderDetails->setFilmProject($filmProject);
$orderDetails->setTotal($amount);
}
if (!empty($recurringDetails)) {
$frequency = $recurringDetails['frequency'];
$totalPeriod = $recurringDetails['totalPeriod'];
$totalPaymentCount = $this->getTotalPaymentCount($frequency, $totalPeriod);
$recurringDonation = (new RecurringDonation)
->setFrequency($frequency)
->setCurrentPaymentCount(0)
->setTotalPaymentCount($totalPaymentCount)
;
$donation
->setRecurringDonation($recurringDonation)
->setIsRecurringDonation(true)
;
}
return $donation;
}
/**
* Event after persist donation object
*
* @param Donation $entityInstance
* @return void
*/
public function dispatchPersistEntity($entityInstance, $isDafSupport = false)
{
// Enable donation email if it's null
if (empty($entityInstance->isIsDonationEmail()) && $entityInstance->isIsDonationEmail() !== false) {
$entityInstance->setIsDonationEmail(true);
}
$this->em->persist($entityInstance);
$this->em->flush();
$orderDetails = $entityInstance->getOrderDetails();
$user = $orderDetails->getUser();
if (empty($orderDetails->getXeroId())) {
$this->createInvoice($entityInstance, $user); // XERO process here
}
if ($isDafSupport) { // IF support-our-work
if (empty($orderDetails->getSalesforceId())) {
$this->createDafSalesforceObject($entityInstance, true);
}
if ($entityInstance->isIsDonationEmail()) {
$this->sendDafDonationEmail($entityInstance);
}
} else { // ELSE default donation
$filmProject = $entityInstance->getFilmProject();
if (empty($orderDetails->getSalesforceId())) {
$this->createSalesforceObject($entityInstance);
}
if ($orderDetails->getUser()) {
$filmProject->addUser($orderDetails->getUser());
}
$filmProject->addTotalDonation($orderDetails->getTotal()); // Update total donation on selected film project
$this->em->persist($filmProject);
$this->em->persist($entityInstance);
$this->em->flush();
if ($entityInstance->isIsDonationEmail()) { // Sends email first
$this->sendEmail($entityInstance);
}
$this->createWordpressObject($entityInstance);
}
}
/**
* create empty daf donation obj function
*
* @param [type] $user
* @param integer $supportdaf
* @param array|null $recurringDetails
* @return Donation
*/
public function createDafDonationObject($user, $supportdaf = 0, array $recurringDetails = null, FilmProject $supportProject = null): Donation
{
$currentDatetime = new \DateTime('now');
$orderDetails = $this->orderDetailsService->createDafDonationOrderObj($user, $supportdaf);
$donation = (new Donation())
->setCreatedAt($currentDatetime)
->setModifiedAt($currentDatetime)
->setOrderDetails($orderDetails)
;
if ($supportdaf > 0) {
$dafProduct = $this->settingService->getDefaultDafDonationProduct(); // Set product
if ($supportProject) {
$donation->setFilmProject($supportProject);
$dafProduct = $supportProject->getProduct();
}
if (!empty($recurringDetails) && !empty($supportProject->getRecurringProduct())) { // If recurring donation, use recurring Product
$dafProduct = $supportProject->getRecurringProduct();
}
$orderItems = $orderDetails->getOrderItems();
$orderItems[0]->setProduct($dafProduct);
// NOTE: remove supportdaf to fix recurring donation created twice on Salesforce
// $donation->setSupportdaf($supportdaf);
$orderDetails->setTotal($supportdaf);
}
if (!empty($recurringDetails)) {
$frequency = $recurringDetails['frequency'];
$totalPeriod = $recurringDetails['totalPeriod'];
$totalPaymentCount = $this->getTotalPaymentCount($frequency, $totalPeriod);
$recurringDonation = (new RecurringDonation)
->setFrequency($frequency)
->setCurrentPaymentCount(0)
->setTotalPaymentCount($totalPaymentCount)
;
$donation
->setRecurringDonation($recurringDonation)
->setIsRecurringDonation(true)
;
}
return $donation;
}
/**
* Create Invoice from order details
*
* @param Donation $entityInstance
* @param User|null $user
* @return void
*/
protected function createInvoice(Donation $entityInstance, User $user = null)
{
$orderDetails = $entityInstance->getOrderDetails();
$orderBillingDetails = $orderDetails->getOrderBillingDetails();
$xeroId = null;
if ($entityInstance->isIsDisplayOrganisation() && $orderBillingDetails->getOrganisation() != "") {
$xeroId = $this->processOrganisationContact($orderDetails, $orderBillingDetails);
} else {
$xeroId = $this->processPrimaryContact($orderDetails);
}
$orderDetails = $this->xeroInvoiceService->createDonationInvoice($entityInstance, $xeroId);
if ($xeroId) {
$orderDetails->setXeroContactId($xeroId);
}
$this->em->persist($orderDetails);
$this->em->flush();
}
/**
* Processes Organisation Contact on Xero
*
* @param OrderDetails $orderDetails
* @param OrderBillingDetails $orderBillingDetails
* @return void
*/
private function processOrganisationContact($orderDetails, $orderBillingDetails) {
$xeroId = null;
$organisation = $orderBillingDetails->getOrganisation();
if (!empty($organisation)) {
$xeroId = $this->xeroContactService->getContactOrganisationByOrderDetails($orderDetails);
}
if (empty($xeroId)) {
$xeroId = $this->xeroContactService->createContactOrganisationByOrderDetails($orderDetails);
} else {
// Update the organisation with newly additional person
$this->xeroContactService->updateOrganisationWithAdditionalPersonByOrderDetails($orderDetails, $xeroId);
}
return $xeroId;
}
/**
* Processes Primary Contact on Xero
*
* @param OrderDetails $orderDetails
* @return void
*/
private function processPrimaryContact($orderDetails) {
$xeroId = $this->xeroContactService->getContactByOrderDetails($orderDetails);
if (empty($xeroId)) {
$xeroId = $this->xeroContactService->createContactByOrderDetails($orderDetails);
}
return $xeroId;
}
public function createSalesforceObject(Donation $entityInstance, $isDafSupport = false)
{
// $popupDonationId = '701Bn0000095BSLIA2'; // Staging site
$popupDonationId = '7010K000001eqWhQAI'; // Production
if ($entityInstance->getSupportdaf()) {
$id = $this->salesforceService->createDafDonationTransaction($entityInstance, $popupDonationId);
if ($id) {
$entityInstance->setSupportDafSalesforce($id);
$this->em->persist($entityInstance);
$this->em->flush();
}
}
$id = $this->salesforceService->createDonationTransaction($entityInstance);
if ($id) {
$orderDetails = $entityInstance->getOrderDetails();
$orderDetails->setSalesforceId($id);
$this->em->persist($orderDetails);
$this->em->flush();
}
}
protected function createDafSalesforceObject($entityInstance, $isDafSupport = false)
{
$id = $this->salesforceService->createDafDonationTransaction($entityInstance);
if ($id) {
$orderDetails = $entityInstance->getOrderDetails();
$orderDetails->setSalesforceId($id);
$this->em->persist($orderDetails);
$this->em->flush();
}
}
public function sendInvoice(Donation $entityInstance) {
$this->sendEmail($entityInstance);
}
protected function sendEmail($entityInstance)
{
$orderDetails = $entityInstance->getOrderDetails();
// $attachmentPath = $this->xeroInvoiceService->getInvoiceAsPdf($orderDetails->getXeroId());
$pdfExporter = new PdfExporter($this->twig, $this->pdf);
$orderDetails->setDonation($entityInstance);
$attachmentPath = $pdfExporter->convertDonationReceiptPdf($orderDetails, true, true);
$this->mailService->sendDonationEmail(Constant::MAIL_DONATION_RECEIVED, $entityInstance, $attachmentPath);
}
protected function sendDafDonationEmail($entityInstance)
{
$orderDetails = $entityInstance->getOrderDetails();
$pdfExporter = new PdfExporter($this->twig, $this->pdf);
$orderDetails->setDonation($entityInstance);
$attachmentPath = $pdfExporter->convertDonationReceiptPdf($orderDetails, true, true);
// $attachmentPath = $this->xeroInvoiceService->getInvoiceAsPdf($orderDetails->getXeroId());
$this->mailService->sendDonationEmail(Constant::MAIL_DAF_DONATION_RECEIVED, $entityInstance, $attachmentPath);
}
public function createWordpressObject(Donation $entityInstance)
{
$this->wpService->updateFilmObjectDonation($entityInstance);
}
/**
* Proceed payment event for donation
*
* @param Donation $entityInstance
* @param [type] $usedCardID
* @param [type] $isAddCard
* @return void
*/
public function proceedPayment(Donation $entityInstance, $usedCardID = null, $isAddCard = null)
{
$this->em->persist($entityInstance); // need to save details first before getting dispatched
$this->em->flush();
$orderDetails = $entityInstance->getOrderDetails();
if ($usedCardID && $usedCardID != '') { // If use previous credit card
$this->mwService->processCardByCardId($orderDetails, $usedCardID);
} else {
if (!$isAddCard) {
$isAddCard = 0;
}
$orderDetails = $this->mwService->processCard($orderDetails, $isAddCard, false);
$orderBillingDetails = $orderDetails->getOrderBillingDetails();
if (!empty($orderBillingDetails->getCardId())){ // Save credit card to DB
$user = $orderDetails->getUser();
$this->userService->saveUserCreditCard($user, $orderBillingDetails->getCardId());
$this->orderDetailsService->addCardIDtoBillingOrder($orderDetails, $orderBillingDetails->getCardId());
}
}
$paymentDetails = $orderDetails->getPaymentDetails();
$paymentDetails->setPaid();
$this->updateOrderDetails($orderDetails);
}
/**
* Update recurring donation
*
* @param Donation $entityInstance
* @return Donation
*/
public function updateRecurringDonation(Donation $entityInstance): Donation
{
$recurringDonation = $entityInstance->getRecurringDonation();
$newPaymentcount = $recurringDonation->getCurrentPaymentCount() + 1;
$recurringDonation->setCurrentPaymentCount($newPaymentcount);
$frequency = $recurringDonation->getFrequency();
$nextDatetime = new \DateTime('today');
switch($frequency) {
case 'weekly':
$nextDatetime = $nextDatetime->modify('+ 1week');
break;
case 'monthly':
$nextDatetime = $nextDatetime->modify('+ 1month');
break;
}
$orderDetails = $entityInstance->getOrderDetails();
$orderBillingDetails = $orderDetails->getOrderBillingDetails();
if ($newPaymentcount == 1) { // Means this is the start date of the payment
$recurringDonation->setStartPaymentDate($nextDatetime);
}
$recurringDonation->setNextPaymentDate($nextDatetime);
if ($orderBillingDetails->getCardId()) {
$recurringDonation->setCardId($orderBillingDetails->getCardId());
}
$this->em->persist($recurringDonation);
$this->em->flush();
return $entityInstance;
}
public function updateRecurringDonationTimeline(Donation $entityInstance): Donation
{
$recurringDonation = $entityInstance->getRecurringDonation();
$newPaymentcount = $recurringDonation->getCurrentPaymentCount() + 1;
$recurringDonation->setCurrentPaymentCount($newPaymentcount);
$frequency = $recurringDonation->getFrequency();
$nextDatetime = new \DateTime('today');
switch($frequency) {
case 'weekly':
$nextDatetime = $nextDatetime->modify('+ 1week');
break;
case 'monthly':
$nextDatetime = $nextDatetime->modify('+ 1month');
break;
}
if ($newPaymentcount == 1) { // Means this is the start date of the payment
$recurringDonation->setStartPaymentDate(new \DateTime('today'));
}
$recurringDonation->setNextPaymentDate($nextDatetime);
$this->em->persist($recurringDonation);
$this->em->flush();
return $entityInstance;
}
/**
* Get total payment count based on parameters given
*
* @param string $frequency
* @param integer $period
* @return int
*/
protected function getTotalPaymentCount(string $frequency, int $period): int
{
$totalPaymentCount = 0;
switch($frequency){
case 'weekly':
$totalPaymentCount = $period * 4;
break;
case 'monthly':
$totalPaymentCount = $period * 1;
break;
}
return $totalPaymentCount;
}
/**
* Clone donation with order details from existing recurring donation
*
* @param Donation $donation
* @return Donation
*/
public function recreateDonationForRecurringDonation(Donation $donation): Donation
{
$orderDetails = $donation->getOrderDetails();
// Order billing details
$newPaymentDetails = clone $orderDetails->getPaymentDetails();
$this->em->persist($newPaymentDetails);
$newOrderBillingDetails = clone $orderDetails->getOrderBillingDetails();
$this->em->persist($newOrderBillingDetails);
$newOrderDetails = clone $donation->getOrderDetails();
$newOrderDetails
->setOrderBillingDetails($newOrderBillingDetails)
->setPaymentDetails($newPaymentDetails)
->setCreatedAt(new \DateTime('now'))
->setModifiedAt(new \DateTime('now'))
->setSalesforceId(null)
;
$this->em->persist($newOrderDetails);
$newDonation = clone $donation;
$newDonation->setOrderDetails($newOrderDetails);
$newDonation = $this->updateNewDonationObj($newDonation);
$this->em->persist($newDonation);
$this->em->flush();
return $newDonation;
}
public function refundDonation(Donation $donation)
{
$orderDetails = $donation->getOrderDetails();
$this->mwService->refund($orderDetails); // Merchant Warrior
$this->salesforceService->refundTransaction($orderDetails); // Salesforce update
if ($donation->getSupportDafSalesforce()) {
$this->salesforceService->refundDafSupportTransaction($donation); // Salesforce popup update
}
$xeroContactId = $orderDetails->getXeroContactId(); // Xero Refund update
if (!empty($xeroContactId)) $this->xeroInvoiceService->createDonationRefundInvoice($donation, $xeroContactId);
$this->updateOrderDetails($orderDetails);
$filmProject = $donation->getFilmProject();
if ($filmProject) {
$donationRepository = $this->em->getRepository(Donation::class);
$donations = $donationRepository->findDonationsByProjectOrderDate($filmProject->getId());
$totalDonation = 0;
foreach ($donations as $currentDonation) {
$currentOrderDetails = $currentDonation->getOrderDetails();
$currentAmount = $currentOrderDetails->getTotal();
// Need to exclude popup amount if there is one
$supportdaf = $currentDonation->getSupportDaf();
if ($supportdaf) {
$currentAmount -= $supportdaf;
}
$totalDonation += $currentAmount;
}
$filmProject->setTotalDonation($totalDonation);
$this->em->persist($filmProject);
$this->em->flush();
if (!$filmProject->isIsDafCore()) $this->wpService->updateFilmObjectDonation($donation); // Finally update wordpress end
}
}
public function partialRefundDonation(Donation $donation)
{
$refundAmount = '' . number_format(((float)$donation->getSupportdaf() / 100), 2, '.', '');
$orderDetails = $donation->getOrderDetails();
$this->mwService->partialRefund($orderDetails, $refundAmount); // Merchant Warrior
if ($donation->getSupportDafSalesforce()) {
$this->salesforceService->refundDafSupportTransaction($donation); // Salesforce update
}
if (!empty($xeroContactId)) $this->xeroInvoiceService->createPartialDonationRefundInvoice($donation, $xeroContactId);
$this->updateOrderDetails($orderDetails);
}
public function checkRecurringDonationPastCampaign($donation): bool
{
return false;
}
public function appearAsAnonymous(Donation $donation)
{
$donation->setAsAnonymous();
$this->em->persist($donation);
$this->em->flush();
$this->createWordpressObject($donation);
}
public function appearAsNonAnonymous(Donation $donation)
{
$donation->setAsNonAnonymous();
$this->em->persist($donation);
$this->em->flush();
$this->createWordpressObject($donation);
}
public function setAmountVisibility(Donation $donation, bool $isInvisible)
{
$donation->setIsAmountInvisible($isInvisible);
$this->em->persist($donation);
$this->em->flush();
$this->createWordpressObject($donation);
}
protected function updateNewDonationObj(Donation $donation): Donation
{
$currentDatetime = new \DateTime('today');
$donation->setCreatedAt($currentDatetime);
$donation->setModifiedAt($currentDatetime);
return $donation;
}
protected function updateOrderDetails($orderDetails)
{
$orderDetails->setModifiedAt(new \DateTime('now'));
$this->em->persist($orderDetails);
$this->em->flush();
}
public function updateDonation(Donation $donation, $oldProjectId = null)
{
$donationRepository = $this->em->getRepository(Donation::class);
$orderDetails = $donation->getOrderDetails();
if ($orderDetails->getXeroContactId()) { // Update xero contact details
$this->xeroContactService->updateContactById($orderDetails, $orderDetails->getXeroContactId());
}
$amount = $orderDetails->getTotal() / 100; // Update xero donation amount
if ($orderDetails->getXeroId()){
$this->xeroInvoiceService->updateInvoice($donation, $orderDetails->getXeroId(), $amount, $oldProjectId);
}
if ($orderDetails->getSalesforceId()) {
$this->salesforceService->updateTransaction($orderDetails); // update Salesforce
} else {
$id = $this->salesforceService->createDonationTransaction($donation);
if ($id) {
$orderDetails->setSalesforceId((string)$id);
$this->em->persist($orderDetails);
$this->em->flush();
}
}
$this->wpService->updateFilmObjectDonation($donation); // Update total wordpress object
$filmProject = $donation->getFilmProject();
if ($filmProject) {
$donations = $donationRepository->findDonationsByProjectOrderDate($filmProject->getId());
$this->updateDonationTotalEntity($donations, $filmProject);
}
if ($oldProjectId) { // Added workflow to update previous project to remove the donation
$filmProjectRepository = $this->em->getRepository(FilmProject::class);
$oldProject = $filmProjectRepository->findOneBy(['id' => $oldProjectId]);
if ($oldProject) {
$this->wpService->updateFilmObjectDonationByProject($oldProject); // Update previous project page
$donations = $donationRepository->findDonationsByProjectOrderDate($oldProjectId); // Update project in the system
$this->updateDonationTotalEntity($donations, $oldProject);
}
}
}
public function archiveDonation(Donation $donation)
{
$this->wpService->updateFilmObjectDonation($donation); // Update total wordpress object
$filmProject = $donation->getFilmProject();
if ($filmProject) {
$donationRepository = $this->em->getRepository(Donation::class);
$donations = $donationRepository->findDonationsByProjectOrderDate($filmProject->getId());
$totalDonation = 0;
foreach ($donations as $currentDonation) {
$currentOrderDetails = $currentDonation->getOrderDetails();
$currentAmount = $currentOrderDetails->getTotal();
// Need to exclude popup amount if there is one
$supportdaf = $currentDonation->getSupportDaf();
if ($supportdaf) {
$currentAmount -= $supportdaf;
}
$totalDonation += $currentAmount;
}
$filmProject->setTotalDonation($totalDonation);
$this->em->persist($filmProject);
$this->em->flush();
}
}
private function updateDonationTotalEntity(array $donations, FilmProject $filmProject) {
$totalDonation = 0;
foreach ($donations as $currentDonation) {
$currentOrderDetails = $currentDonation->getOrderDetails();
$totalDonation += $currentOrderDetails->getTotal() / 100;
}
$filmProject->setTotalDonation($totalDonation * 100);
$this->em->persist($filmProject);
$this->em->flush();
}
}