Your IP : 216.73.216.224


Current Path : /var/www/html/administrator/components/com_osmembership/controller/
Upload File :
Current File : /var/www/html/administrator/components/com_osmembership/controller/subscription.php

<?php
/**
 * @package        Joomla
 * @subpackage     Membership Pro
 * @author         Tuan Pham Ngoc
 * @copyright      Copyright (C) 2012 - 2026 Ossolution Team
 * @license        GNU/GPL, see LICENSE.php
 */

defined('_JEXEC') or die;

use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\Database\DatabaseDriver;
use Joomla\Database\ParameterType;
use Joomla\Utilities\ArrayHelper;
use OSSolution\MembershipPro\Admin\Event\Export\BeforeXLSXExport;
use OSSolution\MembershipPro\Admin\Event\Subscriptions\AfterImportSubscriptions;
use OSSolution\MembershipPro\Admin\Event\Subscriptions\BeforeExportSubscriptions;
use OSSolution\MembershipPro\Admin\Event\Subscriptions\SubscriptionsExport;

/**
 * OSMembership Plugin controller
 *
 * @package        Joomla
 * @subpackage     Membership Pro
 */
class OSMembershipControllerSubscription extends OSMembershipController
{
	use MPFControllerDownload;

	/**
	 * Cancel recurring subscription
	 *
	 * @throws Exception
	 */
	public function cancel_subscription()
	{
		$id = $this->input->post->getInt('id', 0);

		/* @var DatabaseDriver $db */
		$db    = Factory::getContainer()->get('db');
		$query = $db->getQuery(true);
		$query->select('*')
			->from('#__osmembership_subscribers')
			->where('id = ' . $id);
		$db->setQuery($query);
		$rowSubscription = $db->loadObject();

		if ($rowSubscription && OSMembershipHelper::canCancelSubscription($rowSubscription))
		{
			JLoader::register(
				'OSMembershipModelRegister',
				JPATH_ROOT . '/components/com_osmembership/model/register.php'
			);

			/**@var OSMembershipModelRegister $model * */
			$model = $this->getModel('Register');
			$ret   = $model->cancelSubscription($rowSubscription);

			if ($ret)
			{
				$this->setRedirect(
					$this->getViewItemUrl($rowSubscription->id),
					Text::_('OSM_SUBSCRIPTION_CANCELLED')
				);
			}
			else
			{
				// Redirect back to profile page, the payment plugin should enqueue the reason of failed cancellation so that it could be displayed to end user
				$this->setRedirect($this->getViewItemUrl($rowSubscription->id));
			}
		}
		else
		{
			throw new InvalidArgumentException(Text::_('OSM_INVALID_RECURRING_SUBSCRIPTION'), 404);
		}
	}

	/**
	 * Resend confirmation email to registrants in case they didn't receive it
	 */
	public function resend_email()
	{
		$cid = $this->input->get('cid', [], 'array');
		$cid = ArrayHelper::toInteger($cid);

		/* @var OSMembershipModelSubscription $model */
		$model = $this->getModel();

		foreach ($cid as $id)
		{
			$model->resendEmail($id);
		}

		$this->setRedirect($this->getViewListUrl(), Text::_('OSM_EMAIL_SUCCESSFULLY_RESENT'));
	}

	/**
	 * Send batch mail to subscriptions
	 */
	public function batch_mail()
	{
		if ($this->app->isClient('site'))
		{
			$this->csrfProtection();
		}

		$this->checkAccessPermission('subscriptions');

		/* @var OSMembershipModelSubscription $model */
		$model = $this->getModel();

		try
		{
			$model->batchMail($this->input);
			$this->setMessage(Text::_('OSM_BATCH_MAIL_SUCCESS'));
		}
		catch (Exception $e)
		{
			$this->setMessage($e->getMessage(), 'error');
		}

		$this->setRedirect($this->getViewListUrl());
	}

	/**
	 * Send batch mail to subscriptions
	 */
	public function batch_subscriptions()
	{
		if ($this->app->isClient('site'))
		{
			$this->csrfProtection();
		}

		$this->checkAccessPermission('subscriptions');

		/* @var OSMembershipModelSubscription $model */
		$model = $this->getModel();

		try
		{
			$model->batchSubscriptions($this->input);
			$this->setMessage(Text::_('OSM_BATCH_SUBSCRIPTIONS_SUCCESS'));
		}
		catch (Exception $e)
		{
			$this->setMessage($e->getMessage(), 'error');
		}

		$this->setRedirect($this->getViewListUrl());
	}

	/**
	 * Send batch SMS to selected subscriptions
	 *
	 * @return void
	 */
	public function batch_sms()
	{
		$cid     = $this->input->get('cid', [], 'array');
		$cid     = ArrayHelper::toInteger($cid);
		$message = $this->getInput()->getString('sms_message');

		/* @var OSMembershipModelSubscription $model */
		$model = $this->getModel();

		try
		{
			$model->batchSMS($cid, $message);

			$this->setMessage(Text::_('OSM_BATCH_SMS_SUCCESS'));
		}
		catch (Exception $e)
		{
			$this->setMessage($e->getMessage(), 'warning');
		}

		$this->setRedirect($this->getViewListUrl());
	}

	/**
	 * Renew subscription for given user
	 */
	public function renew()
	{
		if ($this->app->isClient('site'))
		{
			$this->csrfProtection();
		}

		$this->checkAccessPermission('subscriptions');

		$cid = $this->input->get('cid', [], 'array');
		$cid = ArrayHelper::toInteger($cid);

		/* @var OSMembershipModelSubscription $model */
		$model = $this->getModel('subscription');

		foreach ($cid as $id)
		{
			$model->renew($id);
		}

		$this->setRedirect($this->getViewListUrl(), Text::_('OSM_SUBSCRIPTIONS_RENEWED'));
	}

	/**
	 * Import Subscribers from CSV
	 */
	public function import()
	{
		if ($this->app->isClient('site'))
		{
			throw new Exception('You are not allowed to perform this action', 403);
		}

		$this->checkAccessPermission('subscriptions');

		$inputFile = $this->input->files->get('input_file');
		$fileName  = $inputFile ['name'];
		$fileExt   = strtolower(OSMembershipHelper::getFileExt($fileName));

		if (!in_array($fileExt, ['csv', 'xls', 'xlsx']))
		{
			$this->setRedirect(
				'index.php?option=com_osmembership&view=import',
				Text::_('Invalid File Type. Only CSV, XLS and XLS file types are supported')
			);

			return;
		}

		/* @var OSMembershipModelImport $model */
		$model = $this->getModel('import');

		try
		{
			$ids = $model->store($inputFile['tmp_name'], $inputFile['name']);

			// Backward compatible
			if (is_int($ids))
			{
				$numberSubscribers = $ids;
			}
			else
			{
				$numberSubscribers = count($ids);
			}

			$event = new AfterImportSubscriptions([
				'ids' => $ids,
			]);

			$this->getApplication()->getDispatcher()->dispatch($event->getName(), $event);

			$this->setRedirect(
				$this->getViewListUrl(),
				Text::sprintf('OSM_NUMBER_SUBSCRIBERS_IMPORTED', $numberSubscribers)
			);
		}
		catch (Exception $e)
		{
			$this->setRedirect('index.php?option=com_osmembership&view=import');
			$this->setMessage($e->getMessage(), 'error');
		}
	}

	/**
	 * Import Subscribers from Joomla cores
	 */
	public function import_from_joomla()
	{
		if ($this->app->isClient('site'))
		{
			throw new Exception('You are not allowed to perform this action', 403);
		}

		$planId = $this->input->getInt('to_plan_id', 0);
		$start  = $this->input->getInt('start', 0);
		$limit  = $this->input->getInt('limit', 0);

		if (empty($planId))
		{
			throw new Exception('Plan not found', 404);
		}

		/* @var OSMembershipModelImport $model */
		$model = $this->getModel('import');

		try
		{
			$numberSubscribers = $model->importFromJoomla($planId, $start, $limit);
			$this->setRedirect(
				$this->getViewListUrl(),
				Text::sprintf('OSM_NUMBER_SUBSCRIBERS_IMPORTED', $numberSubscribers)
			);
		}
		catch (Exception $e)
		{
			$this->setRedirect('index.php?option=com_osmembership&view=import');
			$this->setMessage($e->getMessage(), 'error');
		}
	}

	/**
	 * Export subscriptions into a CSV file
	 */
	public function export()
	{
		$this->checkAccessPermission('subscriptions');

		set_time_limit(0);

		$config = OSMembershipHelper::getConfig();

		/* @var OSMembershipModelSubscriptions $model */

		if ($this->app->isClient('site'))
		{
			$model = $this->getModel('subscribers');
			$model->setParams($this->app->getParams());
		}
		else
		{
			$model = $this->getModel('subscriptions');
		}

		$model->set('limitstart', 0)
			->set('limit', 0);

		if ($config->include_group_members_in_export)
		{
			$model->setIncludeGroupMembers(true);
		}

		if ($config->export_exclude_status)
		{
			$model->setExcludeStatus(explode(',', $config->export_exclude_status));
		}

		$cid = $this->input->getString('cid', '');

		// This is needed in case there are filter fields on subscriptions management
		if (is_string($cid))
		{
			$cid = explode(',', $cid);
		}

		if (count($cid))
		{
			$model->set('filter_subscription_ids', $cid);
		}

		// Give a chance for plugin to handle export subscriptions itself
		PluginHelper::importPlugin('osmembership');

		$event = new BeforeExportSubscriptions(['model' => $model]);

		$results = $this->getApplication()->getDispatcher()->dispatch($event->getName(), $event)->getArgument('result', []);

		if (count($results) && $filename = $results[0])
		{
			// There is a plugin handles export, it returns the filename, so we just process download the file
			$this->processDownloadFile($filename);

			return;
		}

		// OK, no plugin handles export, we do the work ourself
		$rows = $model->getData();

		$numberSubscriptions = count($rows);

		if ($numberSubscriptions == 0)
		{
			$this->setMessage(Text::_('There are no subscription records to export'));
			$this->setRedirect($this->getViewListUrl());

			return;
		}

		$planId = (int) $model->get('plan_id');

		$event = new SubscriptionsExport([
			'planId'              => $planId,
			'numberSubscriptions' => $numberSubscriptions,
		]);

		$this->getApplication()->getDispatcher()->dispatch($event->getName(), $event);

		/* @var DatabaseDriver $db */
		$db    = Factory::getContainer()->get('db');
		$query = $db->getQuery(true)
			->select('id, name, title, is_core')
			->from('#__osmembership_fields')
			->where('published = 1')
			->where('hide_on_export = 0')
			->order('ordering');

		if ($planId > 0)
		{
			$negPlanId = -1 * $planId;
			$query->where(
				'(plan_id = 0 OR id IN (SELECT field_id FROM #__osmembership_field_plan WHERE plan_id = ' . $planId . ' OR (plan_id < 0 AND plan_id != ' . $negPlanId . ')))'
			);
		}

		$db->setQuery($query);
		$rowFields = $db->loadObjectList();

		$fieldIds = [];
		foreach ($rowFields as $rowField)
		{
			if ($rowField->is_core)
			{
				continue;
			}

			$fieldIds[] = $rowField->id;
		}

		$fieldValues = $model->getFieldsData($fieldIds);

		$useFieldTitleAsHeader = $config->get('export_column_header_format', 'name') === 'title';

		$exportFields = [
			'id',
			'category',
			'plan',
			'user_id',
			'username',
		];

		$fields  = [];
		$headers = [];

		foreach ($exportFields as $exportField)
		{
			if ($config->get('export_' . $exportField, 1))
			{
				$fields[]  = $exportField;
				$headers[] = Text::_('OSM_' . strtoupper($exportField));
			}
		}

		$i = 0;

		foreach ($rowFields as $rowField)
		{
			$fields[]  = $rowField->name;
			$headers[] = $rowField->title;

			if ($rowField->is_core)
			{
				unset($rowFields[$i]);
			}

			$i++;
		}

		$exportFields = [
			'created_date',
			'payment_date',
			'from_date',
			'to_date',
			'published',
			'amount',
			'tax_amount',
			'discount_amount',
			'gross_amount',
			'payment_method',
			'transaction_id',
			'membership_id',
			'subscription_type',
		];

		// Language item map for fields which not follow the normal convention
		$languageItemMap = [
			'from_date' => 'OSM_SUBSCRIPTION_START_DATE',
			'to_date'   => 'OSM_SUBSCRIPTION_END_DATE',
			'published' => 'OSM_SUBSCRIPTION_STATUS',
			'amount'    => 'OSM_NET_AMOUNT',
		];

		foreach ($exportFields as $exportField)
		{
			if ($config->get('export_' . $exportField, 1))
			{
				$fields[] = $exportField;

				if (isset($languageItemMap[$exportField]))
				{
					$headers[] = Text::_($languageItemMap[$exportField]);
				}
				else
				{
					$headers[] = Text::_('OSM_' . strtoupper($exportField));
				}
			}
		}

		if ($config->export_subscribe_to_newsletter)
		{
			$fields[]  = 'subscribe_newsletter';
			$headers[] = Text::_('OSM_SUBSCRIBE_TO_NEWSLETTER');
		}

		if ($config->activate_invoice_feature && $config->get('export_invoice_number', 1))
		{
			$fields[]  = 'invoice_number';
			$headers[] = Text::_('OSM_INVOICE_NUMBER');
		}

		if ($config->enable_coupon && $config->get('export_coupon', 1))
		{
			$fields[]  = 'coupon_code';
			$headers[] = Text::_('OSM_COUPON');
		}

		$this->prepareExportData($rows, $rowFields, $fieldValues, $fields);

		if ($exportTemplateId = $this->input->getInt('export_template', 0))
		{
			$exportTemplate = $model->getExportTemplate($exportTemplateId);

			if ($exportTemplate->fields)
			{
				$templateFields = json_decode($exportTemplate->fields, true);
				$fieldHeaderMap = array_combine($fields, $headers);
				$newFields      = [];
				$newHeaders     = [];

				foreach ($templateFields as $templateField)
				{
					if (isset($fieldHeaderMap[$templateField]))
					{
						$newFields[]  = $templateField;
						$newHeaders[] = $fieldHeaderMap[$templateField];
					}
				}

				$fields  = $newFields;
				$headers = $newHeaders;
			}
		}

		if ($useFieldTitleAsHeader)
		{
			$subscriptionStatusMapping = [
				0 => Text::_('OSM_PENDING'),
				1 => Text::_('OSM_ACTIVE'),
				2 => Text::_('OSM_EXPIRED'),
				3 => Text::_('OSM_CANCELLED_PENDING'),
				4 => Text::_('OSM_CANCELLED_REFUNDED'),
			];

			// Build payment method title mapping
			$paymentMethodTitleMapping = [];
			$query->clear()
				->select('name, title')
				->from('#__osmembership_plugins');
			$db->setQuery($query);

			foreach ($db->loadObjectList() as $paymentMethod)
			{
				$paymentMethodTitleMapping[$paymentMethod->name] = Text::_($paymentMethod->title);
			}

			foreach ($rows as &$row)
			{
				$row->published = $subscriptionStatusMapping[$row->published] ?? $row->published;
				$row->payment_method = $paymentMethodTitleMapping[$row->payment_method] ?? $row->payment_method;
			}
		}

		$filePath = OSMembershipHelper::callOverridableHelperMethod(
			'Data',
			'excelExport',
			[$fields, $rows, 'subscriptions_list', $useFieldTitleAsHeader ? $headers : []]
		);

		if ($filePath)
		{
			$this->processDownloadFile($filePath);
		}
	}

	/**
	 * Export registrants into a PDF file
	 */
	public function export_pdf()
	{
		if ($this->app->isClient('site'))
		{
			$this->csrfProtection();
		}

		$this->checkAccessPermission('subscriptions');

		set_time_limit(0);

		$config = OSMembershipHelper::getConfig();

		/* @var OSMembershipModelSubscriptions $model */
		if ($this->app->isClient('site'))
		{
			$model = $this->getModel('subscribers');

			// Set menu item params
			$model->setParams($this->app->getParams());
		}
		else
		{
			$model = $this->getModel('subscriptions');
		}

		$model->set('limitstart', 0)
			->set('limit', 0);

		if ($config->include_group_members_in_export)
		{
			$model->setIncludeGroupMembers(true);
		}

		if ($config->export_exclude_status)
		{
			$model->setExcludeStatus(explode(',', $config->export_exclude_status));
		}

		$cid = $this->input->get('cid', [], 'array');

		if (count($cid))
		{
			$model->set('filter_subscription_ids', $cid);
		}

		$rows = $model->getData();

		$numberSubscriptions = count($rows);

		if ($numberSubscriptions == 0)
		{
			$this->setMessage(Text::_('There are no subscription records to export'));
			$this->setRedirect($this->getViewListUrl());

			return;
		}

		$planId = (int) $model->get('plan_id');

		$event = new SubscriptionsExport([
			'planId'              => $planId,
			'numberSubscriptions' => $numberSubscriptions,
		]);

		$this->getApplication()->getDispatcher()->dispatch($event->getName(), $event);

		/* @var DatabaseDriver $db */
		$db    = Factory::getContainer()->get('db');
		$query = $db->getQuery(true)
			->select('id, name, is_core')
			->from('#__osmembership_fields')
			->where('published = 1')
			->where('hide_on_export = 0')
			->order('ordering');

		if ($planId > 0)
		{
			$negPlanId = -1 * $planId;
			$query->where(
				'(plan_id = 0 OR id IN (SELECT field_id FROM #__osmembership_field_plan WHERE plan_id = ' . $planId . ' OR (plan_id < 0 AND plan_id != ' . $negPlanId . ')))'
			);
		}

		$db->setQuery($query);
		$rowFields = $db->loadObjectList();

		$fieldIds = [];

		foreach ($rowFields as $rowField)
		{
			if ($rowField->is_core)
			{
				continue;
			}

			$fieldIds[] = $rowField->id;
		}

		$fieldValues = $model->getFieldsData($fieldIds);

		$fields = [
			'id',
			'plan',
			'user_id',
			'username',
		];

		$i = 0;

		foreach ($rowFields as $rowField)
		{
			$fields[] = $rowField->name;

			if ($rowField->is_core)
			{
				unset($rowFields[$i]);
			}

			$i++;
		}

		$fields = array_merge($fields, [
			'created_date',
			'payment_date',
			'from_date',
			'to_date',
			'published',
			'amount',
			'tax_amount',
			'discount_amount',
			'gross_amount',
			'payment_method',
			'transaction_id',
			'membership_id',
		]);

		if ($config->activate_invoice_feature)
		{
			$fields[] = 'invoice_number';
		}

		if ($config->enable_coupon)
		{
			$fields[] = 'coupon_code';
		}

		$this->prepareExportData($rows, $rowFields, $fieldValues, $fields);

		$filePath = OSMembershipHelper::callOverridableHelperMethod(
			'Helper',
			'generateSubscriptionsPDF',
			[$rows, $fields]
		);

		$this->processDownloadFile($filePath);
	}

	/**
	 * Export PDF invoices
	 */
	public function export_invoices()
	{
		ini_set('memory_limit', '-1');
		set_time_limit(0);

		if ($this->app->isClient('site'))
		{
			$this->csrfProtection();
		}

		$this->checkAccessPermission('subscriptions');

		set_time_limit(0);

		$config = OSMembershipHelper::getConfig();

		/* @var OSMembershipModelSubscriptions $model */

		if ($this->app->isClient('site'))
		{
			$model = $this->getModel('subscribers');
		}
		else
		{
			$model = $this->getModel('subscriptions');
		}

		$model->set('limitstart', 0)
			->set('limit', 0);

		if ($config->include_group_members_in_export)
		{
			$model->setIncludeGroupMembers(true);
		}

		if ($config->export_exclude_status)
		{
			$model->setExcludeStatus(explode(',', $config->export_exclude_status));
		}

		$cid = $this->input->get('cid', [], 'array');

		if (count($cid))
		{
			$model->set('filter_subscription_ids', $cid);
		}

		// Only export subscriptions with real invoices
		$model->getQuery()->where('tbl.invoice_number > 0');

		$rows = $model->getData();

		$numberSubscriptions = count($rows);

		if ($numberSubscriptions == 0)
		{
			$this->setMessage(Text::_('There are no subscription records to export'));
			$this->setRedirect($this->getViewListUrl());

			return;
		}

		$filePath = OSMembershipHelper::callOverridableHelperMethod('Helper', 'generateSubscriptionsInvoices', [$rows]);

		$this->processDownloadFile($filePath);
	}

	/**
	 * Method to export expired subscribers in the whole system
	 *
	 * @return void
	 */
	public function export_expired_subscribers()
	{
		$this->checkAccessPermission('subscriptions');

		set_time_limit(0);

		/* @var OSMembershipModelSubscriptions $model */
		$model = $this->getModel('subscriptions');

		$rows = $model->getExpiredSubscribers();

		if (count($rows) == 0)
		{
			$this->setMessage(Text::_('There are no expired subscribers to export'));
			$this->setRedirect($this->getViewListUrl());

			return;
		}

		/* @var DatabaseDriver $db */
		$db    = Factory::getContainer()->get('db');
		$query = $db->getQuery(true)
			->select('id, name, is_core')
			->from('#__osmembership_fields')
			->where('published = 1')
			->where('plan_id = 0')
			->where('hide_on_export = 0')
			->order('ordering');
		$db->setQuery($query);
		$rowFields = $db->loadObjectList();

		$fieldIds = [];

		foreach ($rowFields as $rowField)
		{
			if ($rowField->is_core)
			{
				continue;
			}

			$fieldIds[] = $rowField->id;
		}

		$userIds = [];

		foreach ($rows as $row)
		{
			$userIds[] = $row->user_id;
		}

		// Get latest expired plan
		$query->clear()
			->select('a.title, b.user_id, b.to_date')
			->from('#__osmembership_plans AS a')
			->innerJoin('#__osmembership_subscribers AS b On a.id = b.plan_id')
			->whereIn('b.user_id', $userIds)
			->where('b.published = 2')
			->order('b.to_date');
		$db->setQuery($query);
		$userPlans = $db->loadObjectList('user_id');

		$fieldValues = $model->getFieldsData($fieldIds);

		$fields = [
			'username',
			'plan',
		];

		$i = 0;

		foreach ($rowFields as $rowField)
		{
			$fields[] = $rowField->name;

			if ($rowField->is_core)
			{
				unset($rowFields[$i]);
			}

			$i++;
		}

		$fields[] = 'created_date';
		$fields[] = 'to_date';
		$fields[] = 'membership_id';

		$dateFields = ['created_date', 'to_date'];

		foreach ($rows as $row)
		{
			$row->plan    = $userPlans[$row->user_id]->title;
			$row->to_date = $userPlans[$row->user_id]->to_date;

			foreach ($dateFields as $dateField)
			{
				if ((int) $row->{$dateField})
				{
					$row->{$dateField} = HTMLHelper::_('date', $row->{$dateField}, 'Y-m-d');
				}
				else
				{
					$row->{$dateField} = '';
				}
			}

			foreach ($rowFields as $rowField)
			{
				if (!$rowField->is_core)
				{
					$fieldValue             = $fieldValues[$row->id][$rowField->id] ?? '';
					$row->{$rowField->name} = $fieldValue;
				}
			}
		}

		// Give a chance for plugin to handle export data itself
		PluginHelper::importPlugin('osmembership');

		$event = new BeforeXLSXExport([
			'rows'     => $rows,
			'fields'   => $fields,
			'filename' => 'expired_subscribers_list.xlsx',
		]);

		$results = $this->getApplication()->getDispatcher()->dispatch($event->getName(), $event)->getArgument('result', []);

		if (count($results) && $filename = $results[0])
		{
			// There is a plugin handles export, it return the filename, so we just process download the file
			$this->processDownloadFile($filename);

			return;
		}

		$filePath = OSMembershipHelper::callOverridableHelperMethod(
			'Data',
			'excelExport',
			[$fields, $rows, 'expired_subscribers_list']
		);

		if ($filePath)
		{
			$this->processDownloadFile($filePath);
		}
	}

	/**
	 * Generate CSV Template use to import subscribers into the system
	 */
	public function csv_import_template()
	{
		$this->checkAccessPermission('subscriptions');

		$planId = $this->input->getInt('plan_id', 0);

		/* @var DatabaseDriver $db */
		$db    = Factory::getContainer()->get('db');
		$query = $db->getQuery(true);
		$query->select('name')
			->from('#__osmembership_fields')
			->whereNotIn('fieldtype', ['Heading', 'Message'], ParameterType::STRING)
			->where('published = 1')
			->order('ordering');

		if ($planId > 0)
		{
			$negPlanId = -1 * $planId;
			$query->where(
				'(plan_id = 0 OR id IN (SELECT field_id FROM #__osmembership_field_plan WHERE plan_id = ' . $planId . ' OR plan_id < 0))'
			)
				->where(
					'id NOT IN (SELECT field_id FROM #__osmembership_field_plan WHERE plan_id = ' . $negPlanId . ')'
				);
		}

		$db->setQuery($query);
		$rowFields = $db->loadObjectList();

		$fields = [
			'plan',
			'username',
			'password',
		];

		foreach ($rowFields as $rowField)
		{
			$fields[] = $rowField->name;
		}

		$fields[] = 'created_date';
		$fields[] = 'payment_date';
		$fields[] = 'from_date';
		$fields[] = 'to_date';
		$fields[] = 'published';
		$fields[] = 'amount';
		$fields[] = 'tax_amount';
		$fields[] = 'discount_amount';
		$fields[] = 'gross_amount';
		$fields[] = 'payment_method';
		$fields[] = 'transaction_id';
		$fields[] = 'membership_id';

		$row           = new stdClass();
		$row->plan     = '6 Months Membership';
		$row->username = 'tuanpn';
		$row->password = 'tuanpn';

		foreach ($rowFields as $rowField)
		{
			if ($rowField->name == 'first_name')
			{
				$row->{$rowField->name} = 'Tuan';
			}
			elseif ($rowField->name == 'last_name')
			{
				$row->{$rowField->name} = 'Pham Ngoc';
			}
			elseif ($rowField->name == 'email')
			{
				$row->{$rowField->name} = 'tuanpn@joomdonation.com';
			}
			else
			{
				$row->{$rowField->name} = 'sample_data_for_' . $rowField->name;
			}
		}

		$todayDate = Factory::getDate();

		$row->payment_date = $row->from_date = $row->created_date = $todayDate->format('Y-m-d');

		$todayDate->modify('+6 months');

		$row->to_date         = $todayDate->format('Y-m-d');
		$row->published       = 1;
		$row->amount          = 100;
		$row->tax_amount      = 10;
		$row->discount_amount = 0;
		$row->gross_amount    = 110;
		$row->payment_method  = 'os_paypal';
		$row->transaction_id  = 'TR4756RUI78465';
		$row->membership_id   = 1001;

		// Give a chance for plugin to handle export data itself
		PluginHelper::importPlugin('osmembership');

		$event = new BeforeXLSXExport([
			'rows'     => [$row],
			'fields'   => $fields,
			'filename' => 'subscriptions_import_template.xlsx',
		]);

		$results = $this->getApplication()->getDispatcher()->dispatch($event->getName(), $event)->getArgument('result', []);

		if (count($results) && $filename = $results[0])
		{
			// There is a plugin handles export, it returns the filename, so we just process download the file
			$this->processDownloadFile($filename);

			return;
		}

		$filePath = OSMembershipHelper::callOverridableHelperMethod(
			'Data',
			'excelExport',
			[$fields, [$row], 'subscriptions_import_template']
		);

		if ($filePath)
		{
			$this->processDownloadFile($filePath);
		}
	}

	/**
	 * Disable reminders for selected subscription records
	 */
	public function disable_reminders()
	{
		if ($this->app->isClient('site'))
		{
			$this->csrfProtection();
		}

		$this->checkAccessPermission('subscriptions');

		$cid = $this->input->post->get('cid', [], 'array');

		if (count($cid))
		{
			/* @var DatabaseDriver $db */
			$db    = Factory::getContainer()->get('db');
			$query = $db->getQuery(true);
			$query->update('#__osmembership_subscribers')
				->set('first_reminder_sent = 1')
				->set('second_reminder_sent = 1')
				->set('third_reminder_sent = 1')
				->whereIn('id', $cid);

			$subscribeTableFields = array_keys($db->getTableColumns('#__osmembership_subscribers'));

			$extraReminderSentFields = [
				'fourth_reminder_sent',
				'fifth_reminder_sent',
				'sixth_reminder_sent',
			];

			foreach ($extraReminderSentFields as $extraField)
			{
				if (in_array($extraField, $subscribeTableFields))
				{
					$query->set($extraField . ' = 1');
				}
			}

			$db->setQuery($query)
				->execute();
		}

		$this->setRedirect(
			$this->getViewListUrl(),
			Text::_('OSM_REMINDER_EMAILS_DISABLED_FOR_SELECTED_SUBSCRIPTIONS')
		);
	}

	/**
	 * Enable reminders for selected subscription records
	 */
	public function enable_reminders(): void
	{
		if ($this->app->isClient('site'))
		{
			$this->csrfProtection();
		}

		$this->checkAccessPermission('subscriptions');

		$cid = $this->input->post->get('cid', [], 'array');

		if (count($cid))
		{
			/* @var DatabaseDriver $db */
			$db    = Factory::getContainer()->get('db');
			$query = $db->getQuery(true);
			$query->update('#__osmembership_subscribers')
				->set('first_reminder_sent = 0')
				->set('second_reminder_sent = 0')
				->set('third_reminder_sent = 0')
				->set('first_reminder_sent_at = NULL')
				->set('second_reminder_sent_at = NULL')
				->set('third_reminder_sent_at = NULL')
				->whereIn('id', $cid);

			$subscribeTableFields = array_keys($db->getTableColumns('#__osmembership_subscribers'));

			$extraReminderSentFields = [
				'fourth_reminder_sent',
				'fifth_reminder_sent',
				'sixth_reminder_sent',
			];

			foreach ($extraReminderSentFields as $extraField)
			{
				if (in_array($extraField, $subscribeTableFields))
				{
					$query->set($extraField . ' = 0')
						->set($extraField . '_at = NULL');
				}
			}

			$db->setQuery($query)
				->execute();
		}

		$this->setRedirect(
			$this->getViewListUrl(),
			Text::_('OSM_REMINDER_EMAILS_DISABLED_FOR_SELECTED_SUBSCRIPTIONS')
		);
	}

	/**
	 * Cancel recurring subscription
	 *
	 * @throws Exception
	 */
	public function refund()
	{
		$id = $this->input->post->getInt('id', 0);

		/* @var DatabaseDriver $db */
		$db    = Factory::getContainer()->get('db');
		$query = $db->getQuery(true);
		$query->select('*')
			->from('#__osmembership_subscribers')
			->where('id = ' . $id);
		$db->setQuery($query);
		$rowSubscription = $db->loadObject();

		if (OSMembershipHelper::canRefundSubscription($rowSubscription))
		{
			/**@var OSMembershipModelSubscription $model * */
			$model = $this->getModel('Subscription');

			try
			{
				$model->refund($rowSubscription);

				$this->setRedirect(
					$this->getViewItemUrl($rowSubscription->id),
					Text::_('OSM_SUBSCRIPTION_REFUNDED')
				);
			}
			catch (Exception $e)
			{
				$this->app->enqueueMessage($e->getMessage(), 'error');
				$this->setRedirect(
					$this->getViewItemUrl($rowSubscription->id),
					$e->getMessage(),
					'error'
				);
			}
		}
		else
		{
			throw new InvalidArgumentException(Text::_('OSM_CANNOT_PROCESS__REFUND'));
		}
	}

	/**
	 * Send payment request to selected subscriptions
	 *
	 * @return void
	 */
	public function request_payment()
	{
		if (!$this->app->getIdentity()->authorise('membershippro.subscriptions', 'com_osmembership'))
		{
			throw new Exception('You do not have permission to request payment', 403);
		}

		$cid = $this->input->get('cid', [], 'array');
		$cid = ArrayHelper::toInteger($cid);

		/* @var OSMembershipModelSubscription $model */
		$model = $this->getModel();

		try
		{
			foreach ($cid as $id)
			{
				$model->sendPaymentRequestEmail($id);
			}

			$this->setMessage(Text::_('OSM_REQUEST_PAYMENT_EMAIL_SENT_SUCCESSFULLY'));
		}
		catch (Exception $e)
		{
			$this->setMessage($e->getMessage(), 'warning');
		}

		$this->setRedirect($this->getViewListUrl());
	}

	/**
	 * Download member card
	 */
	public function download_member_card()
	{
		if (!$this->app->getIdentity()->authorise('membershippro.subscriptions', 'com_osmembership'))
		{
			throw new Exception('You do not have permission to request payment', 403);
		}

		$config = OSMembershipHelper::getConfig();

		if (!$config->activate_member_card_feature)
		{
			throw new Exception(
				'This feature is not enabled. If you are administrator and want to use it, go to Membership Pro -> Configuration to enable this feature',
				403
			);
		}

		$id = $this->input->getInt('id');

		/* @var DatabaseDriver $db */
		$db    = Factory::getContainer()->get('db');
		$query = $db->getQuery(true);
		$query->select('a.*, b.username')
			->from('#__osmembership_subscribers AS a')
			->leftJoin('#__users AS b ON a.user_id = b.id')
			->where('a.id = ' . $id);
		$db->setQuery($query);
		$item = $db->loadObject();

		if (!$item)
		{
			$this->setRedirect($this->getViewListUrl(), Text::_('Invalid Subscription Record'));

			return;
		}

		// Generate member card and save it
		$path = OSMembershipHelperSubscription::generatePlanMemberCard($item, $config);

		$this->processDownloadFile($path, $item->username . '_' . $item->plan_id . '.pdf');
	}

	/**
	 * Get sales chart data used for reloading chart
	 */
	public function get_sales_chart_data()
	{
		$planId = $this->input->getInt('plan_id');

		$sales = OSMembershipModelSubscriptions::getLast12MonthSales($planId);

		$data = [
			'labels'             => $sales['labels'],
			'sales'              => $sales['income'],
			'subscriptionsCount' => $sales['count'],
		];

		echo json_encode($data);

		$this->app->close();
	}

	/**
	 * Get daily sales data
	 *
	 * @return void
	 */
	public function get_daily_sales_chart_data()
	{
		$planId   = $this->input->getInt('plan_id');
		$fromDate = $this->input->getString('from_date', '');
		$toDate   = $this->input->getString('to_date', '');

		/* @var OSMembershipModelSubscriptions $model */
		$model = MPFModel::getTempInstance('Subscriptions', 'OSMembershipModel')
			->set('plan_id', $planId)
			->set('filter_from_date', $fromDate)
			->set('filter_to_date', $toDate);

		$sales = $model->getDailySalesStatistic();

		$data = [
			'labels'             => $sales['labels'],
			'sales'              => $sales['income'],
			'subscriptionsCount' => $sales['count'],
		];

		echo json_encode($data);

		$this->app->close();
	}

	/**
	 * Prepare data for each subscription record before it is being exported
	 *
	 * @param   array  $rows
	 * @param   array  $rowFields
	 * @param   array  $fieldValues
	 * @param   array  $fields
	 *
	 * @return void
	 */
	protected function prepareExportData($rows, $rowFields, $fieldValues, &$fields): void
	{
		$config = OSMembershipHelper::getConfig();

		$dateFields = ['created_date', 'payment_date', 'from_date', 'to_date'];

		$subscriptionTypeMapping = [
			'subscribe' => Text::_('OSM_NEW_SUBSCRIPTION'),
			'renew'     => Text::_('OSM_SUBSCRIPTION_RENEWAL'),
			'upgrade'   => Text::_('OSM_SUBSCRIPTION_UPGRADE'),
		];

		foreach ($rows as $row)
		{
			foreach ($rowFields as $rowField)
			{
				if (!$rowField->is_core)
				{
					$fieldValue             = $fieldValues[$row->id][$rowField->id] ?? '';
					$row->{$rowField->name} = $fieldValue;
				}
			}

			if ($config->activate_invoice_feature && $row->invoice_number > 0)
			{
				$row->invoice_number = OSMembershipHelper::formatInvoiceNumber($row, $config);
			}
			else
			{
				$row->invoice_number = '';
			}

			$row->plan     = $row->plan_title;
			$row->category = $row->category_title;

			foreach ($dateFields as $dateField)
			{
				if ((int) $row->{$dateField})
				{
					$row->{$dateField} = HTMLHelper::_('date', $row->{$dateField}, $config->date_format);
				}
				else
				{
					$row->{$dateField} = '';
				}
			}

			$row->subscription_type = $subscriptionTypeMapping[$row->act] ?? Text::_('OSM_NEW_SUBSCRIPTION');
		}
	}
}