diff --git a/CRM/Report/Form/Contribute/Detail.php b/CRM/Report/Form/Contribute/Detail.php
new file mode 100644
index 0000000000000000000000000000000000000000..5774577239740eac872ecc2adb06eb78ce027ab6
--- /dev/null
+++ b/CRM/Report/Form/Contribute/Detail.php
@@ -0,0 +1,1104 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ */
+class CRM_Report_Form_Contribute_Detail extends CRM_Report_Form {
+
+  protected $_summary = NULL;
+
+  protected $_softFrom = NULL;
+
+  protected $noDisplayContributionOrSoftColumn = FALSE;
+
+  protected $_customGroupExtends = [
+    'Contact',
+    'Individual',
+    'Contribution',
+  ];
+
+  protected $groupConcatTested = TRUE;
+
+  protected $isTempTableBuilt = FALSE;
+
+  /**
+   * Query mode.
+   *
+   * This can be 'Main' or 'SoftCredit' to denote which query we are building.
+   *
+   * @var string
+   */
+  protected $queryMode = 'Main';
+
+  /**
+   * Is this report being run on contributions as the base entity.
+   *
+   * The report structure is generally designed around a base entity but
+   * depending on input it can be run in a sort of hybrid way that causes a lot
+   * of complexity.
+   *
+   * If it is in isContributionsOnlyMode we can simplify.
+   *
+   * (arguably there should be 2 separate report templates, not one doing double duty.)
+   *
+   * @var bool
+   */
+  protected $isContributionBaseMode = FALSE;
+
+  /**
+   * This report has been optimised for group filtering.
+   *
+   * @var bool
+   * @see https://issues.civicrm.org/jira/browse/CRM-19170
+   */
+  protected $groupFilterNotOptimised = FALSE;
+
+  /**
+   * Class constructor.
+   */
+  public function __construct() {
+    $this->_autoIncludeIndexedFieldsAsOrderBys = 1;
+    $this->_columns = array_merge(
+      $this->getColumns('Contact', [
+        'order_bys_defaults' => ['sort_name' => 'ASC '],
+        'fields_defaults' => ['sort_name'],
+        'fields_excluded' => ['id'],
+        'fields_required' => ['id'],
+        'filters_defaults' => ['is_deleted' => 0],
+        'no_field_disambiguation' => TRUE,
+      ]),
+      [
+        'civicrm_email' => [
+          'dao' => 'CRM_Core_DAO_Email',
+          'fields' => [
+            'email' => [
+              'title' => ts('Donor Email'),
+              'default' => TRUE,
+            ],
+          ],
+          'grouping' => 'contact-fields',
+        ],
+        'civicrm_line_item' => [
+          'dao' => 'CRM_Price_DAO_LineItem',
+        ],
+        'civicrm_phone' => [
+          'dao' => 'CRM_Core_DAO_Phone',
+          'fields' => [
+            'phone' => [
+              'title' => ts('Donor Phone'),
+              'default' => TRUE,
+              'no_repeat' => TRUE,
+            ],
+          ],
+          'grouping' => 'contact-fields',
+        ],
+        'civicrm_contribution' => [
+          'dao' => 'CRM_Contribute_DAO_Contribution',
+          'fields' => [
+            'contribution_id' => [
+              'name' => 'id',
+              'no_display' => TRUE,
+              'required' => TRUE,
+            ],
+            'list_contri_id' => [
+              'name' => 'id',
+              'title' => ts('Contribution ID'),
+            ],
+            'financial_type_id' => [
+              'title' => ts('Financial Type'),
+              'default' => TRUE,
+            ],
+            'contribution_status_id' => [
+              'title' => ts('Contribution Status'),
+            ],
+            'contribution_page_id' => [
+              'title' => ts('Contribution Page'),
+            ],
+            'source' => [
+              'title' => ts('Source'),
+            ],
+            'payment_instrument_id' => [
+              'title' => ts('Payment Type'),
+            ],
+            'check_number' => [
+              'title' => ts('Check Number'),
+            ],
+            'trxn_id' => NULL,
+            'receive_date' => ['default' => TRUE],
+            'receipt_date' => NULL,
+            'thankyou_date' => NULL,
+            'non_deductible_amount' => [
+              'title' => ts('Non-deductible Amount'),
+            ],
+            'fee_amount' => NULL,
+            'net_amount' => NULL,
+            'contribution_or_soft' => [
+              'title' => ts('Contribution OR Soft Credit?'),
+              'dbAlias' => "'Contribution'",
+            ],
+            'soft_credits' => [
+              'title' => ts('Soft Credits'),
+              'dbAlias' => "NULL",
+            ],
+            'soft_credit_for' => [
+              'title' => ts('Soft Credit For'),
+              'dbAlias' => "NULL",
+            ],
+            'cancel_date' => [
+              'title' => ts('Cancelled / Refunded Date'),
+              'name' => 'contribution_cancel_date',
+            ],
+            'cancel_reason' => [
+              'title' => ts('Cancellation / Refund Reason'),
+            ],
+          ],
+          'filters' => [
+            'contribution_or_soft' => [
+              'title' => ts('Contribution OR Soft Credit?'),
+              'clause' => "(1)",
+              'operatorType' => CRM_Report_Form::OP_SELECT,
+              'type' => CRM_Utils_Type::T_STRING,
+              'options' => [
+                'contributions_only' => ts('Contributions Only'),
+                'soft_credits_only' => ts('Soft Credits Only'),
+                'both' => ts('Both'),
+              ],
+              'default' => 'contributions_only',
+            ],
+            'receive_date' => ['operatorType' => CRM_Report_Form::OP_DATE],
+            'receipt_date' => ['operatorType' => CRM_Report_Form::OP_DATE],
+            'thankyou_date' => ['operatorType' => CRM_Report_Form::OP_DATE],
+            'contribution_source' => [
+              'title' => ts('Source'),
+              'name' => 'source',
+              'type' => CRM_Utils_Type::T_STRING,
+            ],
+            'currency' => [
+              'title' => ts('Currency'),
+              'operatorType' => CRM_Report_Form::OP_MULTISELECT,
+              'options' => CRM_Core_OptionGroup::values('currencies_enabled'),
+              'default' => NULL,
+              'type' => CRM_Utils_Type::T_STRING,
+            ],
+            'non_deductible_amount' => [
+              'title' => ts('Non-deductible Amount'),
+            ],
+            'financial_type_id' => [
+              'title' => ts('Financial Type'),
+              'operatorType' => CRM_Report_Form::OP_MULTISELECT,
+              'options' => CRM_Contribute_BAO_Contribution::buildOptions('financial_type_id', 'search'),
+              'type' => CRM_Utils_Type::T_INT,
+            ],
+            'contribution_page_id' => [
+              'title' => ts('Contribution Page'),
+              'operatorType' => CRM_Report_Form::OP_MULTISELECT,
+              'options' => CRM_Contribute_PseudoConstant::contributionPage(),
+              'type' => CRM_Utils_Type::T_INT,
+            ],
+            'payment_instrument_id' => [
+              'title' => ts('Payment Type'),
+              'operatorType' => CRM_Report_Form::OP_MULTISELECT,
+              'options' => CRM_Contribute_PseudoConstant::paymentInstrument(),
+              'type' => CRM_Utils_Type::T_INT,
+            ],
+            'contribution_status_id' => [
+              'title' => ts('Contribution Status'),
+              'operatorType' => CRM_Report_Form::OP_MULTISELECT,
+              'options' => CRM_Contribute_BAO_Contribution::buildOptions('contribution_status_id', 'search'),
+              'default' => [1],
+              'type' => CRM_Utils_Type::T_INT,
+            ],
+            'total_amount' => ['title' => ts('Contribution Amount')],
+            'cancel_date' => [
+              'title' => ts('Cancelled / Refunded Date'),
+              'operatorType' => CRM_Report_Form::OP_DATE,
+              'name' => 'contribution_cancel_date',
+            ],
+            'cancel_reason' => [
+              'title' => ts('Cancellation / Refund Reason'),
+            ],
+          ],
+          'order_bys' => [
+            'financial_type_id' => ['title' => ts('Financial Type')],
+            'contribution_status_id' => ['title' => ts('Contribution Status')],
+            'payment_instrument_id' => ['title' => ts('Payment Method')],
+            'receive_date' => ['title' => ts('Date Received')],
+            'receipt_date' => ['title' => ts('Receipt Date')],
+            'thankyou_date' => ['title' => ts('Thank-you Date')],
+          ],
+          'group_bys' => [
+            'contribution_id' => [
+              'name' => 'id',
+              'title' => ts('Contribution'),
+            ],
+          ],
+          'grouping' => 'contri-fields',
+        ],
+        'civicrm_contribution_soft' => [
+          'dao' => 'CRM_Contribute_DAO_ContributionSoft',
+          'fields' => [
+            'soft_credit_type_id' => ['title' => ts('Soft Credit Type')],
+            'soft_credit_amount' => ['title' => ts('Soft Credit amount'), 'name' => 'amount', 'type' => CRM_Utils_Type::T_MONEY],
+          ],
+          'filters' => [
+            'soft_credit_type_id' => [
+              'title' => ts('Soft Credit Type'),
+              'operatorType' => CRM_Report_Form::OP_MULTISELECT,
+              'options' => CRM_Core_OptionGroup::values('soft_credit_type'),
+              'default' => NULL,
+              'type' => CRM_Utils_Type::T_STRING,
+            ],
+          ],
+          'group_bys' => [
+            'soft_credit_id' => [
+              'name' => 'id',
+              'title' => ts('Soft Credit'),
+            ],
+          ],
+        ],
+        'civicrm_financial_trxn' => [
+          'dao' => 'CRM_Financial_DAO_FinancialTrxn',
+          'fields' => [
+            'financial_trxn_id' => [
+              'name' => 'id',
+              'no_display' => TRUE,
+              'required' => TRUE,
+            ],
+            'total_amount' => [
+              'title' => ts('Total Amount'),
+              'required' => TRUE,
+            ],
+            'currency' => [
+              'title' => 'Currency',
+              'required' => TRUE,
+              'no_display' => TRUE,
+            ],
+            'trxn_date' => [
+              'title' => ts('Transaction Date'),
+            ],
+            'status_id' => [
+              'title' => ts('Status'),
+            ],
+            'card_type_id' => [
+              'title' => ts('Credit Card Type'),
+            ],
+          ],
+          'filters' => [
+            'card_type_id' => [
+              'title' => ts('Credit Card Type'),
+              'operatorType' => CRM_Report_Form::OP_MULTISELECT,
+              'options' => CRM_Financial_DAO_FinancialTrxn::buildOptions('card_type_id'),
+              'default' => NULL,
+              'type' => CRM_Utils_Type::T_STRING,
+            ],
+            'trxn_date' => [
+              'title' => ts('Transaction Date'),
+'type' => CRM_Utils_Type::T_DATE,
+              'operatorType' => CRM_Report_Form::OP_DATE,
+            ],
+          ],
+          'group_bys' => [
+            'financial_trxn_id' => [
+              'name' => 'id',
+              'required' => TRUE,
+              'default' => TRUE,
+              'title' => ts('Financial Trxn'),
+            ],
+          ],
+        ],
+        'civicrm_batch' => [
+          'dao' => 'CRM_Batch_DAO_EntityBatch',
+          'grouping' => 'contri-fields',
+          'fields' => [
+            'batch_id' => [
+              'name' => 'batch_id',
+              'title' => ts('Batch Name'),
+            ],
+          ],
+          'filters' => [
+            'bid' => [
+              'title' => ts('Batch Name'),
+              'operatorType' => CRM_Report_Form::OP_MULTISELECT,
+              'options' => CRM_Batch_BAO_Batch::getBatches(),
+              'type' => CRM_Utils_Type::T_INT,
+              'dbAlias' => 'batch_civireport.batch_id',
+            ],
+          ],
+        ],
+        'civicrm_contribution_ordinality' => [
+          'dao' => 'CRM_Contribute_DAO_Contribution',
+          'alias' => 'cordinality',
+          'filters' => [
+            'ordinality' => [
+              'title' => ts('Contribution Ordinality'),
+              'operatorType' => CRM_Report_Form::OP_MULTISELECT,
+              'options' => [
+                0 => ts('First by Contributor'),
+                1 => ts('Second or Later by Contributor'),
+              ],
+              'type' => CRM_Utils_Type::T_INT,
+            ],
+          ],
+        ],
+        'civicrm_note' => [
+          'dao' => 'CRM_Core_DAO_Note',
+          'fields' => [
+            'contribution_note' => [
+              'name' => 'note',
+              'title' => ts('Contribution Note'),
+            ],
+          ],
+          'filters' => [
+            'note' => [
+              'name' => 'note',
+              'title' => ts('Contribution Note'),
+              'operator' => 'like',
+              'type' => CRM_Utils_Type::T_STRING,
+            ],
+          ],
+        ],
+        'civicrm_pledge_payment' => [
+          'dao' => 'CRM_Pledge_DAO_PledgePayment',
+          'fields' => [
+            'pledge_id' => [
+              'title' => ts('Pledge ID'),
+            ],
+          ],
+          'filters' => [
+            'pledge_id' => [
+              'title' => ts('Pledge ID'),
+              'type' => CRM_Utils_Type::T_INT,
+            ],
+          ],
+        ],
+      ],
+      $this->getColumns('Address')
+    );
+    // The tests test for this variation of the sort_name field. Don't argue with the tests :-).
+    $this->_columns['civicrm_contact']['fields']['sort_name']['title'] = ts('Donor Name');
+    $this->_groupFilter = TRUE;
+    $this->_tagFilter = TRUE;
+    // If we have campaigns enabled, add those elements to both the fields, filters and sorting
+    $this->addCampaignFields('civicrm_contribution', FALSE, TRUE);
+
+    $this->_currencyColumn = 'civicrm_financial_trxn_currency';
+    parent::__construct();
+  }
+
+  /**
+   * Validate incompatible report settings.
+   *
+   * @return bool
+   *   true if no error found
+   */
+  public function validate() {
+    // If you're displaying Contributions Only, you can't group by soft credit.
+    $contributionOrSoftVal = $this->getElementValue('contribution_or_soft_value');
+    if ($contributionOrSoftVal[0] == 'contributions_only') {
+      $groupBySoft = $this->getElementValue('group_bys');
+      if (!empty($groupBySoft['soft_credit_id'])) {
+        $this->setElementError('group_bys', ts('You cannot group by soft credit when displaying contributions only.  Please uncheck "Soft Credit" in the Grouping tab.'));
+      }
+    }
+
+    return parent::validate();
+  }
+
+  /**
+   * Set the FROM clause for the report.
+   */
+  public function from() {
+    $this->setFromBase('civicrm_contact');
+    $this->_from .= "
+      INNER JOIN civicrm_contribution {$this->_aliases['civicrm_contribution']}
+        ON {$this->_aliases['civicrm_contact']}.id = {$this->_aliases['civicrm_contribution']}.contact_id
+        AND {$this->_aliases['civicrm_contribution']}.is_test = 0
+        AND {$this->_aliases['civicrm_contribution']}.is_template = 0
+      ";
+
+    $this->joinContributionToSoftCredit();
+    $this->appendAdditionalFromJoins();
+  }
+
+  /**
+   * @param array $rows
+   *
+   * @return array
+   * @throws \CRM_Core_Exception
+   */
+  public function statistics(&$rows) {
+    $statistics = parent::statistics($rows);
+
+    $totalAmount = $average = $fees = $net = [];
+    $count = 0;
+    $select = "
+        SELECT COUNT(civicrm_financial_trxn_total_amount ) as count,
+               SUM( civicrm_financial_trxn_total_amount ) as amount,
+               ROUND(AVG(civicrm_financial_trxn_total_amount), 2) as avg,
+               stats.currency as currency,
+               SUM( stats.fee_amount ) as fees,
+               SUM( stats.net_amount ) as net
+        ";
+
+    $group = "\nGROUP BY civicrm_financial_trxn_currency";
+    $from = " FROM {$this->temporaryTables['civireport_contribution_detail_temp3']['name']} "
+    . "JOIN civicrm_financial_trxn stats ON {$this->temporaryTables['civireport_contribution_detail_temp3']['name']}.civicrm_financial_trxn_financial_trxn_id = stats.id ";
+    $sql = "{$select} {$from} {$group} ";
+    CRM_Core_DAO::disableFullGroupByMode();
+    $dao = CRM_Core_DAO::executeQuery($sql);
+    CRM_Core_DAO::reenableFullGroupByMode();
+    $this->addToDeveloperTab($sql);
+
+    while ($dao->fetch()) {
+      CRM_Core_Error::debug_var('a', $dao);
+      $totalAmount[] = CRM_Utils_Money::format($dao->amount, $dao->currency) . " (" . $dao->count . ")";
+      $fees[] = CRM_Utils_Money::format($dao->fees, $dao->currency);
+      $net[] = CRM_Utils_Money::format($dao->net, $dao->currency);
+      $average[] = CRM_Utils_Money::format($dao->avg, $dao->currency);
+      $count += $dao->count;
+    }
+    $statistics['counts']['amount'] = [
+      'title' => ts('Total Amount (Contributions)'),
+      'value' => implode(',  ', $totalAmount),
+      'type' => CRM_Utils_Type::T_STRING,
+    ];
+    $statistics['counts']['count'] = [
+      'title' => ts('Total Contributions'),
+      'value' => $count,
+    ];
+    $statistics['counts']['fees'] = [
+      'title' => ts('Fees'),
+      'value' => implode(',  ', $fees),
+      'type' => CRM_Utils_Type::T_STRING,
+    ];
+    $statistics['counts']['net'] = [
+      'title' => ts('Net'),
+      'value' => implode(',  ', $net),
+      'type' => CRM_Utils_Type::T_STRING,
+    ];
+    $statistics['counts']['avg'] = [
+      'title' => ts('Average'),
+      'value' => implode(',  ', $average),
+      'type' => CRM_Utils_Type::T_STRING,
+    ];
+
+    // Stats for soft credits
+    if ($this->_softFrom &&
+      CRM_Utils_Array::value('contribution_or_soft_value', $this->_params) !=
+      'contributions_only'
+    ) {
+      $totalAmount = $average = [];
+      $count = 0;
+      $select = "
+SELECT COUNT(contribution_soft_civireport.amount ) as count,
+       SUM(contribution_soft_civireport.amount ) as amount,
+       ROUND(AVG(contribution_soft_civireport.amount), 2) as avg,
+       {$this->_aliases['civicrm_contribution']}.currency as currency";
+      $sql = "
+{$select}
+{$this->_softFrom}
+GROUP BY {$this->_aliases['civicrm_contribution']}.currency";
+      $dao = CRM_Core_DAO::executeQuery($sql);
+      $this->addToDeveloperTab($sql);
+      while ($dao->fetch()) {
+        $totalAmount[] = CRM_Utils_Money::format($dao->amount, $dao->currency) . " (" .
+          $dao->count . ")";
+        $average[] = CRM_Utils_Money::format($dao->avg, $dao->currency);
+        $count += $dao->count;
+      }
+      $statistics['counts']['softamount'] = [
+        'title' => ts('Total Amount (Soft Credits)'),
+        'value' => implode(',  ', $totalAmount),
+        'type' => CRM_Utils_Type::T_STRING,
+      ];
+      $statistics['counts']['softcount'] = [
+        'title' => ts('Total Soft Credits'),
+        'value' => $count,
+      ];
+      $statistics['counts']['softavg'] = [
+        'title' => ts('Average (Soft Credits)'),
+        'value' => implode(',  ', $average),
+        'type' => CRM_Utils_Type::T_STRING,
+      ];
+    }
+
+    return $statistics;
+  }
+
+  /**
+   * Build the report query.
+   *
+   * @param bool $applyLimit
+   *
+   * @return string
+   */
+  public function buildQuery($applyLimit = FALSE) {
+    if ($this->isTempTableBuilt) {
+      $this->limit();
+      return "SELECT SQL_CALC_FOUND_ROWS * FROM {$this->temporaryTables['civireport_contribution_detail_temp3']['name']} $this->_orderBy $this->_limit";
+    }
+    return parent::buildQuery($applyLimit);
+  }
+
+  /**
+   * Shared function for preliminary processing.
+   *
+   * This is called by the api / unit tests and the form layer and is
+   * the right place to do 'initial analysis of input'.
+   */
+  public function beginPostProcessCommon() {
+    // CRM-18312 - display soft_credits and soft_credits_for column
+    // when 'Contribution or Soft Credit?' column is not selected
+    if (empty($this->_params['fields']['contribution_or_soft'])) {
+      $this->_params['fields']['contribution_or_soft'] = 1;
+      $this->noDisplayContributionOrSoftColumn = TRUE;
+    }
+
+    if (CRM_Utils_Array::value('contribution_or_soft_value', $this->_params) == 'contributions_only') {
+      $this->isContributionBaseMode = TRUE;
+    }
+    if ($this->isContributionBaseMode &&
+      (!empty($this->_params['fields']['soft_credit_type_id'])
+      || !empty($this->_params['soft_credit_type_id_value']))
+    ) {
+      unset($this->_params['fields']['soft_credit_type_id']);
+      if (!empty($this->_params['soft_credit_type_id_value'])) {
+        $this->_params['soft_credit_type_id_value'] = [];
+        CRM_Core_Session::setStatus(ts('Is it not possible to filter on soft contribution type when not including soft credits.'));
+      }
+    }
+    // 1. use main contribution query to build temp table 1
+    $sql = $this->buildQuery();
+    $this->createTemporaryTable('civireport_contribution_detail_temp1', $sql);
+
+    // 2. customize main contribution query for soft credit, and build temp table 2 with soft credit contributions only
+    $this->queryMode = 'SoftCredit';
+    // Rebuild select with no groupby. Do not let column headers change.
+    $headers = $this->_columnHeaders;
+    $this->select();
+    $this->_columnHeaders = $headers;
+    $this->softCreditFrom();
+    // also include custom group from if included
+    // since this might be included in select
+    $this->customDataFrom();
+
+    $select = str_ireplace('contribution_civireport.total_amount', 'contribution_soft_civireport.amount', $this->_select);
+    $select = str_ireplace("'Contribution' as", "'Soft Credit' as", $select);
+
+    // we inner join with temp1 to restrict soft contributions to those in temp1 table.
+    // no group by here as we want to display as many soft credit rows as actually exist.
+    CRM_Utils_Hook::alterReportVar('sql', $this, $this);
+    $sql = "{$select} {$this->_from} {$this->_where} $this->_groupBy";
+    $this->createTemporaryTable('civireport_contribution_detail_temp2', $sql);
+
+    if (CRM_Utils_Array::value('contribution_or_soft_value', $this->_params) ==
+      'soft_credits_only'
+    ) {
+      // revise pager : prev, next based on soft-credits only
+      $this->setPager();
+    }
+
+    // copy _from for later use of stats calculation for soft credits, and reset $this->_from to main query
+    $this->_softFrom = $this->_from;
+
+    // simple reset of ->_from
+    $this->from();
+
+    // also include custom group from if included
+    // since this might be included in select
+    $this->customDataFrom();
+
+    // 3. Decide where to populate temp3 table from
+    if ($this->isContributionBaseMode
+    ) {
+      $this->createTemporaryTable('civireport_contribution_detail_temp3',
+        "(SELECT * FROM {$this->temporaryTables['civireport_contribution_detail_temp1']['name']})"
+      );
+    }
+    elseif (CRM_Utils_Array::value('contribution_or_soft_value', $this->_params) ==
+      'soft_credits_only'
+    ) {
+      $this->createTemporaryTable('civireport_contribution_detail_temp3',
+        "(SELECT * FROM {$this->temporaryTables['civireport_contribution_detail_temp2']['name']})"
+      );
+    }
+    else {
+      $this->createTemporaryTable('civireport_contribution_detail_temp3', "
+(SELECT * FROM {$this->temporaryTables['civireport_contribution_detail_temp1']['name']})
+UNION ALL
+(SELECT * FROM {$this->temporaryTables['civireport_contribution_detail_temp2']['name']})");
+    }
+    $this->isTempTableBuilt = TRUE;
+  }
+
+  /**
+   * Store group bys into array - so we can check elsewhere what is grouped.
+   *
+   * If we are generating a table of soft credits we need to group by them.
+   */
+  protected function storeGroupByArray() {
+    if ($this->queryMode === 'SoftCredit') {
+      $this->_groupByArray = [$this->_aliases['civicrm_contribution_soft'] . '.id'];
+    }
+    else {
+      parent::storeGroupByArray();
+    }
+  }
+
+  /**
+   * Alter display of rows.
+   *
+   * Iterate through the rows retrieved via SQL and make changes for display purposes,
+   * such as rendering contacts as links.
+   *
+   * @param array $rows
+   *   Rows generated by SQL, with an array for each row.
+   */
+  public function alterDisplay(&$rows) {
+    $entryFound = FALSE;
+    $display_flag = $prev_cid = $cid = 0;
+    $contributionTypes = CRM_Contribute_PseudoConstant::financialType();
+    $contributionStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'label');
+    $paymentInstruments = CRM_Contribute_PseudoConstant::paymentInstrument();
+    // We pass in TRUE as 2nd param so that even disabled contribution page titles are returned and replaced in the report
+    $contributionPages = CRM_Contribute_PseudoConstant::contributionPage(NULL, TRUE);
+    $batches = CRM_Batch_BAO_Batch::getBatches();
+    foreach ($rows as $rowNum => $row) {
+      if (!empty($this->_noRepeats) && $this->_outputMode != 'csv') {
+        // don't repeat contact details if its same as the previous row
+        if (array_key_exists('civicrm_contact_id', $row)) {
+          if ($cid = $row['civicrm_contact_id']) {
+            if ($rowNum == 0) {
+              $prev_cid = $cid;
+            }
+            else {
+              if ($prev_cid == $cid) {
+                $display_flag = 1;
+                $prev_cid = $cid;
+              }
+              else {
+                $display_flag = 0;
+                $prev_cid = $cid;
+              }
+            }
+
+            if ($display_flag) {
+              foreach ($row as $colName => $colVal) {
+                // Hide repeats in no-repeat columns, but not if the field's a section header
+                if (in_array($colName, $this->_noRepeats) &&
+                  !array_key_exists($colName, $this->_sections)
+                ) {
+                  unset($rows[$rowNum][$colName]);
+                }
+              }
+            }
+            $entryFound = TRUE;
+          }
+        }
+      }
+
+      if (CRM_Utils_Array::value('civicrm_contribution_contribution_or_soft', $rows[$rowNum]) ==
+        'Contribution'
+      ) {
+        unset($rows[$rowNum]['civicrm_contribution_soft_soft_credit_type_id']);
+      }
+
+      $entryFound = $this->alterDisplayContactFields($row, $rows, $rowNum, 'contribution/detail', ts('View Contribution Details')) ? TRUE : $entryFound;
+      // convert donor sort name to link
+      if (array_key_exists('civicrm_contact_sort_name', $row) &&
+        !empty($rows[$rowNum]['civicrm_contact_sort_name']) &&
+        array_key_exists('civicrm_contact_id', $row)
+      ) {
+        $url = CRM_Utils_System::url("civicrm/contact/view",
+          'reset=1&cid=' . $row['civicrm_contact_id'],
+          $this->_absoluteUrl
+        );
+        $rows[$rowNum]['civicrm_contact_sort_name_link'] = $url;
+        $rows[$rowNum]['civicrm_contact_sort_name_hover'] = ts("View Contact Summary for this Contact.");
+      }
+
+      if ($value = CRM_Utils_Array::value('civicrm_contribution_financial_type_id', $row)) {
+        $rows[$rowNum]['civicrm_contribution_financial_type_id'] = $contributionTypes[$value];
+        $entryFound = TRUE;
+      }
+      if ($value = CRM_Utils_Array::value('civicrm_contribution_contribution_status_id', $row)) {
+        $rows[$rowNum]['civicrm_contribution_contribution_status_id'] = $contributionStatus[$value];
+        $entryFound = TRUE;
+      }
+      if ($value = CRM_Utils_Array::value('civicrm_financial_trxn_status_id', $row)) {
+        $rows[$rowNum]['civicrm_financial_trxn_status_id'] = $contributionStatus[$value];
+        $entryFound = TRUE;
+      }
+      if ($value = CRM_Utils_Array::value('civicrm_contribution_contribution_page_id', $row)) {
+        $rows[$rowNum]['civicrm_contribution_contribution_page_id'] = $contributionPages[$value];
+        $entryFound = TRUE;
+      }
+      if ($value = CRM_Utils_Array::value('civicrm_contribution_payment_instrument_id', $row)) {
+        $rows[$rowNum]['civicrm_contribution_payment_instrument_id'] = $paymentInstruments[$value];
+        $entryFound = TRUE;
+      }
+      if (!empty($row['civicrm_batch_batch_id'])) {
+        $rows[$rowNum]['civicrm_batch_batch_id'] = $batches[$row['civicrm_batch_batch_id']] ?? NULL;
+        $entryFound = TRUE;
+      }
+      if (!empty($row['civicrm_financial_trxn_card_type_id'])) {
+        $rows[$rowNum]['civicrm_financial_trxn_card_type_id'] = $this->getLabels($row['civicrm_financial_trxn_card_type_id'], 'CRM_Financial_DAO_FinancialTrxn', 'card_type_id');
+        $entryFound = TRUE;
+      }
+
+      // Contribution amount links to viewing contribution
+
+      if ($value = CRM_Utils_Array::value('civicrm_contribution_total_amount', $row)) {
+        $rows[$rowNum]['civicrm_contribution_total_amount'] = CRM_Utils_Money::format($value, $row['civicrm_financial_trxn_currency']);
+        if (CRM_Core_Permission::check('access CiviContribute')) {
+          $url = CRM_Utils_System::url(
+            "civicrm/contact/view/contribution",
+            [
+              'reset' => 1,
+              'id' => $row['civicrm_contribution_contribution_id'],
+              'cid' => $row['civicrm_contact_id'],
+              'action' => 'view',
+              'context' => 'contribution',
+              'selectedChild' => 'contribute',
+            ],
+            $this->_absoluteUrl
+          );
+          $rows[$rowNum]['civicrm_contribution_total_amount_link'] = $url;
+          $rows[$rowNum]['civicrm_contribution_total_amount_hover'] = ts("View Details of this Contribution.");
+        }
+        $entryFound = TRUE;
+      }
+
+      // Contribution amount links to viewing contribution
+      if ($value = CRM_Utils_Array::value('civicrm_financial_trxn_total_amount', $row)) {
+        $rows[$rowNum]['civicrm_financial_trxn_total_amount'] = CRM_Utils_Money::format($value, $row['civicrm_financial_trxn_currency']);
+        if (CRM_Core_Permission::check('access CiviContribute')) {
+          $url = CRM_Utils_System::url(
+            "civicrm/contact/view/contribution",
+            [
+              'reset' => 1,
+              'id' => $row['civicrm_contribution_contribution_id'],
+              'cid' => $row['civicrm_contact_id'],
+              'action' => 'view',
+              'context' => 'contribution',
+              'selectedChild' => 'contribute',
+            ],
+            $this->_absoluteUrl
+          );
+          $rows[$rowNum]['civicrm_financial_trxn_total_amount_link'] = $url;
+          $rows[$rowNum]['civicrm_financial_trxn_total_amount_hover'] = ts("View Details of this Contribution.");
+        }
+        $entryFound = TRUE;
+      }
+
+      // convert campaign_id to campaign title
+      if (array_key_exists('civicrm_contribution_campaign_id', $row)) {
+        if ($value = $row['civicrm_contribution_campaign_id']) {
+          $rows[$rowNum]['civicrm_contribution_campaign_id'] = $this->campaigns[$value];
+          $entryFound = TRUE;
+        }
+      }
+
+      // soft credits
+      if (array_key_exists('civicrm_contribution_soft_credits', $row) &&
+        'Contribution' ==
+        CRM_Utils_Array::value('civicrm_contribution_contribution_or_soft', $rows[$rowNum]) &&
+        array_key_exists('civicrm_contribution_contribution_id', $row)
+      ) {
+        $query = "
+SELECT civicrm_contact_id, civicrm_contact_sort_name, civicrm_contribution_total_amount, civicrm_financial_trxn_currency
+FROM   {$this->temporaryTables['civireport_contribution_detail_temp2']['name']}
+WHERE  civicrm_contribution_contribution_id={$row['civicrm_contribution_contribution_id']}";
+        $dao = CRM_Core_DAO::executeQuery($query);
+        $string = '';
+        $separator = ($this->_outputMode !== 'csv') ? "<br/>" : ' ';
+        while ($dao->fetch()) {
+          $url = CRM_Utils_System::url("civicrm/contact/view", 'reset=1&cid=' .
+            $dao->civicrm_contact_id);
+          $string = $string . ($string ? $separator : '') .
+            "<a href='{$url}'>{$dao->civicrm_contact_sort_name}</a> " .
+            CRM_Utils_Money::format($dao->civicrm_contribution_total_amount, $dao->civicrm_financial_trxn_currency);
+        }
+        $rows[$rowNum]['civicrm_contribution_soft_credits'] = $string;
+      }
+
+      if (array_key_exists('civicrm_contribution_soft_credit_for', $row) &&
+        'Soft Credit' ==
+        CRM_Utils_Array::value('civicrm_contribution_contribution_or_soft', $rows[$rowNum]) &&
+        array_key_exists('civicrm_contribution_contribution_id', $row)
+      ) {
+        $query = "
+SELECT civicrm_contact_id, civicrm_contact_sort_name
+FROM   {$this->temporaryTables['civireport_contribution_detail_temp1']['name']}
+WHERE  civicrm_contribution_contribution_id={$row['civicrm_contribution_contribution_id']}";
+        $dao = CRM_Core_DAO::executeQuery($query);
+        $string = '';
+        while ($dao->fetch()) {
+          $url = CRM_Utils_System::url("civicrm/contact/view", 'reset=1&cid=' .
+            $dao->civicrm_contact_id);
+          $string = $string .
+            "\n<a href='{$url}'>{$dao->civicrm_contact_sort_name}</a>";
+        }
+        $rows[$rowNum]['civicrm_contribution_soft_credit_for'] = $string;
+      }
+
+      // CRM-18312 - hide 'contribution_or_soft' column if unchecked.
+      if (!empty($this->noDisplayContributionOrSoftColumn)) {
+        unset($rows[$rowNum]['civicrm_contribution_contribution_or_soft']);
+        unset($this->_columnHeaders['civicrm_contribution_contribution_or_soft']);
+      }
+
+      //convert soft_credit_type_id into label
+      if (array_key_exists('civicrm_contribution_soft_soft_credit_type_id', $rows[$rowNum])) {
+        $rows[$rowNum]['civicrm_contribution_soft_soft_credit_type_id'] = CRM_Core_PseudoConstant::getLabel(
+          'CRM_Contribute_BAO_ContributionSoft',
+          'soft_credit_type_id',
+          $row['civicrm_contribution_soft_soft_credit_type_id']
+        );
+      }
+
+      // Contribution amount links to viewing contribution
+      if ($value = CRM_Utils_Array::value('civicrm_pledge_payment_pledge_id', $row)) {
+        if (CRM_Core_Permission::check('access CiviContribute')) {
+          $url = CRM_Utils_System::url(
+            "civicrm/contact/view/pledge",
+            [
+              'reset' => 1,
+              'id' => $row['civicrm_pledge_payment_pledge_id'],
+              'cid' => $row['civicrm_contact_id'],
+              'action' => 'view',
+              'context' => 'pledge',
+              'selectedChild' => 'pledge',
+            ],
+            $this->_absoluteUrl
+          );
+          $rows[$rowNum]['civicrm_pledge_payment_pledge_id_link'] = $url;
+          $rows[$rowNum]['civicrm_pledge_payment_pledge_id_hover'] = ts("View Details of this Pledge.");
+        }
+        $entryFound = TRUE;
+      }
+
+      $entryFound = $this->alterDisplayAddressFields($row, $rows, $rowNum, 'contribute/detail', 'List all contribution(s) for this ') ? TRUE : $entryFound;
+
+      // skip looking further in rows, if first row itself doesn't
+      // have the column we need
+      if (!$entryFound) {
+        break;
+      }
+      $lastKey = $rowNum;
+    }
+  }
+
+  public function sectionTotals() {
+
+    // Reports using order_bys with sections must populate $this->_selectAliases in select() method.
+    if (empty($this->_selectAliases)) {
+      return;
+    }
+
+    if (!empty($this->_sections)) {
+      // build the query with no LIMIT clause
+      $select = str_ireplace('SELECT SQL_CALC_FOUND_ROWS ', 'SELECT ', $this->_select);
+      $sql = "{$select} {$this->_from} {$this->_where} {$this->_groupBy} {$this->_having} {$this->_orderBy}";
+
+      // pull section aliases out of $this->_sections
+      $sectionAliases = array_keys($this->_sections);
+
+      $ifnulls = [];
+      foreach (array_merge($sectionAliases, $this->_selectAliases) as $alias) {
+        $ifnulls[] = "ifnull($alias, '') as $alias";
+      }
+      $this->_select = "SELECT " . implode(", ", $ifnulls);
+      $this->_select = CRM_Contact_BAO_Query::appendAnyValueToSelect($ifnulls, $sectionAliases);
+
+      /* Group (un-limited) report by all aliases and get counts. This might
+       * be done more efficiently when the contents of $sql are known, ie. by
+       * overriding this method in the report class.
+       */
+
+      $addtotals = '';
+
+      if (array_search("civicrm_contribution_total_amount", $this->_selectAliases) !==
+        FALSE
+      ) {
+        $addtotals = ", sum(civicrm_contribution_total_amount) as sumcontribs";
+        $showsumcontribs = TRUE;
+      }
+
+      $query = $this->_select .
+        "$addtotals, count(*) as ct from {$this->temporaryTables['civireport_contribution_detail_temp3']['name']} group by " .
+        implode(", ", $sectionAliases);
+      // initialize array of total counts
+      $sumcontribs = $totals = [];
+      $dao = CRM_Core_DAO::executeQuery($query);
+      $this->addToDeveloperTab($query);
+      while ($dao->fetch()) {
+
+        // let $this->_alterDisplay translate any integer ids to human-readable values.
+        $rows[0] = $dao->toArray();
+        $this->alterDisplay($rows);
+        $row = $rows[0];
+
+        // add totals for all permutations of section values
+        $values = [];
+        $i = 1;
+        $aliasCount = count($sectionAliases);
+        foreach ($sectionAliases as $alias) {
+          $values[] = $row[$alias];
+          $key = implode(CRM_Core_DAO::VALUE_SEPARATOR, $values);
+          if ($i == $aliasCount) {
+            // the last alias is the lowest-level section header; use count as-is
+            $totals[$key] = $dao->ct;
+            if ($showsumcontribs) {
+              $sumcontribs[$key] = $dao->sumcontribs;
+            }
+          }
+          else {
+            // other aliases are higher level; roll count into their total
+            $totals[$key] = (array_key_exists($key, $totals)) ? $totals[$key] + $dao->ct : $dao->ct;
+            if ($showsumcontribs) {
+              $sumcontribs[$key] = array_key_exists($key, $sumcontribs) ? $sumcontribs[$key] + $dao->sumcontribs : $dao->sumcontribs;
+            }
+          }
+        }
+      }
+      if ($showsumcontribs) {
+        $totalandsum = [];
+        // ts exception to avoid having ts("%1 %2: %3")
+        $title = '%1 contributions / soft-credits: %2';
+
+        if (CRM_Utils_Array::value('contribution_or_soft_value', $this->_params) ==
+          'contributions_only'
+        ) {
+          $title = '%1 contributions: %2';
+        }
+        elseif (CRM_Utils_Array::value('contribution_or_soft_value', $this->_params) ==
+          'soft_credits_only'
+        ) {
+          $title = '%1 soft-credits: %2';
+        }
+        foreach ($totals as $key => $total) {
+          $totalandsum[$key] = ts($title, [
+            1 => $total,
+            2 => CRM_Utils_Money::format($sumcontribs[$key]),
+          ]);
+        }
+        $this->assign('sectionTotals', $totalandsum);
+      }
+      else {
+        $this->assign('sectionTotals', $totals);
+      }
+    }
+  }
+
+  /**
+   * Generate the from clause as it relates to the soft credits.
+   */
+  public function softCreditFrom() {
+
+    $this->_from = "
+      FROM  {$this->temporaryTables['civireport_contribution_detail_temp1']['name']} temp1_civireport
+      INNER JOIN civicrm_contribution {$this->_aliases['civicrm_contribution']}
+        ON temp1_civireport.civicrm_contribution_contribution_id = {$this->_aliases['civicrm_contribution']}.id
+      INNER JOIN civicrm_contribution_soft contribution_soft_civireport
+        ON contribution_soft_civireport.contribution_id = {$this->_aliases['civicrm_contribution']}.id
+      INNER JOIN civicrm_contact      {$this->_aliases['civicrm_contact']}
+        ON {$this->_aliases['civicrm_contact']}.id = contribution_soft_civireport.contact_id
+      {$this->_aclFrom}
+    ";
+
+    //Join temp table if report is filtered by group. This is specific to 'notin' operator and covered in unit test(ref dev/core#212)
+    if (!empty($this->_params['gid_op']) && $this->_params['gid_op'] == 'notin') {
+      $this->joinGroupTempTable('civicrm_contact', 'id', $this->_aliases['civicrm_contact']);
+    }
+    $this->appendAdditionalFromJoins();
+  }
+
+  /**
+   * Append the joins that are required regardless of context.
+   */
+  public function appendAdditionalFromJoins() {
+    if (!empty($this->_params['ordinality_value'])) {
+      $this->_from .= "
+              INNER JOIN (SELECT c.id, IF(COUNT(oc.id) = 0, 0, 1) AS ordinality FROM civicrm_contribution c LEFT JOIN civicrm_contribution oc ON c.contact_id = oc.contact_id AND oc.receive_date < c.receive_date GROUP BY c.id) {$this->_aliases['civicrm_contribution_ordinality']}
+                      ON {$this->_aliases['civicrm_contribution_ordinality']}.id = {$this->_aliases['civicrm_contribution']}.id";
+    }
+    $this->joinPhoneFromContact();
+    $this->joinAddressFromContact();
+    $this->joinEmailFromContact();
+
+    // include contribution note
+    if (!empty($this->_params['fields']['contribution_note']) ||
+      !empty($this->_params['note_value'])
+    ) {
+      $this->_from .= "
+            LEFT JOIN civicrm_note {$this->_aliases['civicrm_note']}
+                      ON ( {$this->_aliases['civicrm_note']}.entity_table = 'civicrm_contribution' AND
+                           {$this->_aliases['civicrm_contribution']}.id = {$this->_aliases['civicrm_note']}.entity_id )";
+    }
+    //for contribution batches
+    if (!empty($this->_params['fields']['batch_id']) ||
+      !empty($this->_params['bid_value'])
+    ) {
+      $this->_from .= "
+        LEFT JOIN civicrm_entity_financial_trxn eft
+          ON eft.entity_id = {$this->_aliases['civicrm_contribution']}.id AND
+            eft.entity_table = 'civicrm_contribution'
+        LEFT JOIN civicrm_entity_batch {$this->_aliases['civicrm_batch']}
+          ON ({$this->_aliases['civicrm_batch']}.entity_id = eft.financial_trxn_id
+          AND {$this->_aliases['civicrm_batch']}.entity_table = 'civicrm_financial_trxn')";
+    }
+    // for credit card type
+    $this->addFinancialTrxnFromClause();
+
+    if ($this->isTableSelected('civicrm_pledge_payment')) {
+      $this->_from .= "
+        LEFT JOIN civicrm_pledge_payment {$this->_aliases['civicrm_pledge_payment']} ON {$this->_aliases['civicrm_pledge_payment']}.contribution_id = {$this->_aliases['civicrm_contribution']}.id
+      ";
+    }
+  }
+
+  /**
+   * Add join to the soft credit table.
+   */
+  protected function joinContributionToSoftCredit() {
+    if (CRM_Utils_Array::value('contribution_or_soft_value', $this->_params) == 'contributions_only'
+      && !$this->isTableSelected('civicrm_contribution_soft')) {
+      return;
+    }
+    $joinType = ' LEFT ';
+    if (CRM_Utils_Array::value('contribution_or_soft_value', $this->_params) == 'soft_credits_only') {
+      $joinType = ' INNER ';
+    }
+    $this->_from .= "
+      $joinType JOIN civicrm_contribution_soft {$this->_aliases['civicrm_contribution_soft']}
+      ON {$this->_aliases['civicrm_contribution_soft']}.contribution_id = {$this->_aliases['civicrm_contribution']}.id
+   ";
+  }
+
+  /**
+   * Add Financial Transaction into From Table if required.
+   */
+  public function addFinancialTrxnFromClause() {
+    if ($this->isTableSelected('civicrm_financial_trxn')) {
+      $this->_from .= "
+         INNER JOIN civicrm_entity_financial_trxn eftcc
+           ON ({$this->_aliases['civicrm_contribution']}.id = eftcc.entity_id AND
+             eftcc.entity_table = 'civicrm_contribution')
+         LEFT JOIN civicrm_financial_trxn {$this->_aliases['civicrm_financial_trxn']}
+           ON {$this->_aliases['civicrm_financial_trxn']}.id = eftcc.financial_trxn_id AND {$this->_aliases['civicrm_financial_trxn']}.is_payment = 1 \n";
+    }
+  }
+
+}
diff --git a/CRM/Report/Form/Contribute/Summary.php b/CRM/Report/Form/Contribute/Summary.php
new file mode 100644
index 0000000000000000000000000000000000000000..5c16588c6c5156cfbf845802db806bccb7690931
--- /dev/null
+++ b/CRM/Report/Form/Contribute/Summary.php
@@ -0,0 +1,1093 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ */
+class CRM_Report_Form_Contribute_Summary extends CRM_Report_Form {
+
+  protected $_customGroupExtends = ['Contribution', 'Contact', 'Individual'];
+  protected $_customGroupGroupBy = TRUE;
+
+  public $_drilldownReport = ['contribute/detail' => 'Link to Detail Report'];
+
+  /**
+   * To what frequency group-by a date column
+   *
+   * @var array
+   */
+  protected $_groupByDateFreq = [
+    'MONTH' => 'Month',
+    'YEARWEEK' => 'Week',
+    'DATE' => 'Day',
+    'QUARTER' => 'Quarter',
+    'YEAR' => 'Year',
+    'FISCALYEAR' => 'Fiscal Year',
+  ];
+
+  /**
+   * This report has been optimised for group filtering.
+   *
+   * @var bool
+   * @see https://issues.civicrm.org/jira/browse/CRM-19170
+   */
+  protected $groupFilterNotOptimised = FALSE;
+
+  /**
+   * Use the generic (but flawed) handling to implement full group by.
+   *
+   * Note that because we are calling the parent group by function we set this to FALSE.
+   * The parent group by function adds things to the group by in order to make the mysql pass
+   * but can create incorrect results in the process.
+   *
+   * @var bool
+   */
+  public $optimisedForOnlyFullGroupBy = FALSE;
+
+  /**
+   * Class constructor.
+   */
+  public function __construct() {
+    $this->_columns = [
+      'civicrm_contact' => [
+        'dao' => 'CRM_Contact_DAO_Contact',
+        'fields' => array_merge(
+          $this->getBasicContactFields(),
+          [
+            'sort_name' => [
+              'title' => ts('Contact Name'),
+              'no_repeat' => TRUE,
+            ],
+          ]
+        ),
+        'filters' => $this->getBasicContactFilters(['deceased' => NULL]),
+        'grouping' => 'contact-fields',
+        'group_bys' => [
+          'id' => ['title' => ts('Contact ID')],
+          'sort_name' => [
+            'title' => ts('Contact Name'),
+          ],
+        ],
+      ],
+      'civicrm_email' => [
+        'dao' => 'CRM_Core_DAO_Email',
+        'fields' => [
+          'email' => [
+            'title' => ts('Email'),
+            'no_repeat' => TRUE,
+          ],
+        ],
+        'grouping' => 'contact-fields',
+      ],
+      'civicrm_line_item' => [
+        'dao' => 'CRM_Price_DAO_LineItem',
+      ],
+      'civicrm_phone' => [
+        'dao' => 'CRM_Core_DAO_Phone',
+        'fields' => [
+          'phone' => [
+            'title' => ts('Phone'),
+            'no_repeat' => TRUE,
+          ],
+        ],
+        'grouping' => 'contact-fields',
+      ],
+      'civicrm_financial_type' => [
+        'dao' => 'CRM_Financial_DAO_FinancialType',
+        'fields' => ['financial_type' => NULL],
+        'grouping' => 'contri-fields',
+        'group_bys' => [
+          'financial_type' => ['title' => ts('Financial Type')],
+        ],
+      ],
+      'civicrm_contribution' => [
+        'dao' => 'CRM_Contribute_DAO_Contribution',
+          //'bao'           => 'CRM_Contribute_BAO_Contribution',
+        'fields' => [
+          'contribution_status_id' => [
+            'title' => ts('Contribution Status'),
+          ],
+          'contribution_source' => ['title' => ts('Source')],
+          'currency' => [
+            'required' => TRUE,
+            'no_display' => TRUE,
+          ],
+          'contribution_page_id' => [
+            'title' => ts('Contribution Page'),
+          ],
+          'total_amount' => [
+            'title' => ts('Contribution Amount Stats'),
+            'default' => TRUE,
+            'statistics' => [
+              'count' => ts('Contributions'),
+              'sum' => ts('Contribution Aggregate'),
+              'avg' => ts('Contribution Avg'),
+            ],
+          ],
+          'non_deductible_amount' => [
+            'title' => ts('Non-deductible Amount'),
+          ],
+          'contribution_recur_id' => [
+            'title' => ts('Contribution Recurring'),
+            'dbAlias' => '!ISNULL(contribution_civireport.contribution_recur_id)',
+            'type' => CRM_Utils_Type::T_BOOLEAN,
+          ],
+        ],
+        'grouping' => 'contri-fields',
+        'filters' => [
+          'receive_date' => ['operatorType' => CRM_Report_Form::OP_DATE],
+          'receipt_date' => ['operatorType' => CRM_Report_Form::OP_DATE],
+          'thankyou_date' => ['operatorType' => CRM_Report_Form::OP_DATE],
+          'contribution_status_id' => [
+            'title' => ts('Contribution Status'),
+            'operatorType' => CRM_Report_Form::OP_MULTISELECT,
+            'options' => CRM_Contribute_BAO_Contribution::buildOptions('contribution_status_id', 'search'),
+            'default' => [1],
+            'type' => CRM_Utils_Type::T_INT,
+          ],
+          'contribution_page_id' => [
+            'title' => ts('Contribution Page'),
+            'operatorType' => CRM_Report_Form::OP_MULTISELECT,
+            'options' => CRM_Contribute_PseudoConstant::contributionPage(),
+            'type' => CRM_Utils_Type::T_INT,
+          ],
+          'currency' => [
+            'title' => ts('Currency'),
+            'operatorType' => CRM_Report_Form::OP_MULTISELECT,
+            'options' => CRM_Core_OptionGroup::values('currencies_enabled'),
+            'default' => NULL,
+            'type' => CRM_Utils_Type::T_STRING,
+          ],
+          'financial_type_id' => [
+            'title' => ts('Financial Type'),
+            'operatorType' => CRM_Report_Form::OP_MULTISELECT,
+            'options' => CRM_Contribute_BAO_Contribution::buildOptions('financial_type_id', 'search'),
+            'type' => CRM_Utils_Type::T_INT,
+          ],
+          'contribution_page_id' => [
+            'title' => ts('Contribution Page'),
+            'operatorType' => CRM_Report_Form::OP_MULTISELECT,
+            'options' => CRM_Contribute_PseudoConstant::contributionPage(),
+            'type' => CRM_Utils_Type::T_INT,
+          ],
+          'contribution_recur_id' => [
+            'title' => ts('Contribution Recurring'),
+            'operatorType' => CRM_Report_Form::OP_SELECT,
+            'type' => CRM_Utils_Type::T_BOOLEAN,
+            'options' => [
+              '' => ts('Any'),
+              TRUE => ts('Yes'),
+              FALSE => ts('No'),
+            ],
+            'dbAlias' => '!ISNULL(contribution_civireport.contribution_recur_id)',
+          ],
+          'total_amount' => [
+            'title' => ts('Contribution Amount'),
+          ],
+          'non_deductible_amount' => [
+            'title' => ts('Non-deductible Amount'),
+          ],
+          'total_sum' => [
+            'title' => ts('Contribution Aggregate'),
+            'type' => CRM_Report_Form::OP_INT,
+            'dbAlias' => 'civicrm_contribution_total_amount_sum',
+            'having' => TRUE,
+          ],
+          'total_count' => [
+            'title' => ts('Contribution Count'),
+            'type' => CRM_Report_Form::OP_INT,
+            'dbAlias' => 'civicrm_contribution_total_amount_count',
+            'having' => TRUE,
+          ],
+          'total_avg' => [
+            'title' => ts('Contribution Avg'),
+            'type' => CRM_Report_Form::OP_INT,
+            'dbAlias' => 'civicrm_contribution_total_amount_avg',
+            'having' => TRUE,
+          ],
+        ],
+        'group_bys' => [
+          'receive_date' => [
+            'frequency' => TRUE,
+            'default' => TRUE,
+            'chart' => TRUE,
+          ],
+          'contribution_source' => NULL,
+          'contribution_status_id' => [
+            'title' => ts('Contribution Status'),
+            'operatorType' => CRM_Report_Form::OP_MULTISELECT,
+            'options' => CRM_Contribute_BAO_Contribution::buildOptions('contribution_status_id', 'search'),
+            'default' => [1],
+            'type' => CRM_Utils_Type::T_INT,
+          ],
+          'contribution_page_id' => [
+            'title' => ts('Contribution Page'),
+            'operatorType' => CRM_Report_Form::OP_MULTISELECT,
+            'options' => CRM_Contribute_PseudoConstant::contributionPage(),
+            'type' => CRM_Utils_Type::T_INT,
+          ],
+          'contribution_recur_id' => [
+            'title' => ts('Contribution Recurring'),
+            'type' => CRM_Utils_Type::T_BOOLEAN,
+            'dbAlias' => '!ISNULL(contribution_civireport.contribution_recur_id)',
+          ],
+        ],
+      ],
+      'civicrm_financial_trxn' => [
+        'dao' => 'CRM_Financial_DAO_FinancialTrxn',
+        'fields' => [
+          'card_type_id' => [
+            'title' => ts('Credit Card Type'),
+            'dbAlias' => 'GROUP_CONCAT(financial_trxn_civireport.card_type_id SEPARATOR ",")',
+          ],
+          'trxn_date' => [
+           'title' => ts('Transaction Date'),
+          ],
+         ],
+        'filters' => [
+          'card_type_id' => [
+            'title' => ts('Credit Card Type'),
+            'operatorType' => CRM_Report_Form::OP_MULTISELECT,
+            'options' => CRM_Financial_DAO_FinancialTrxn::buildOptions('card_type_id'),
+            'default' => NULL,
+            'type' => CRM_Utils_Type::T_STRING,
+          ],
+          'trxn_date' => [
+            'title' => ts('Transaction Date'),
+            'type' => CRM_Utils_Type::T_DATE,
+            'operatorType' => CRM_Report_Form::OP_DATE,
+          ],
+        ],
+      ],
+      'civicrm_batch' => [
+        'dao' => 'CRM_Batch_DAO_EntityBatch',
+        'grouping' => 'contri-fields',
+        'fields' => [
+          'batch_id' => [
+            'name' => 'batch_id',
+            'title' => ts('Batch Title'),
+            'dbAlias' => 'GROUP_CONCAT(DISTINCT batch_civireport.batch_id
+                                    ORDER BY batch_civireport.batch_id SEPARATOR ",")',
+          ],
+        ],
+        'filters' => [
+          'batch_id' => [
+            'title' => ts('Batch Title'),
+            'operatorType' => CRM_Report_Form::OP_MULTISELECT,
+            'options' => CRM_Batch_BAO_Batch::getBatches(),
+            'type' => CRM_Utils_Type::T_INT,
+          ],
+        ],
+        'group_bys' => [
+          'batch_id' => ['title' => ts('Batch Title')],
+        ],
+      ],
+      'civicrm_contribution_soft' => [
+        'dao' => 'CRM_Contribute_DAO_ContributionSoft',
+        'fields' => [
+          'soft_amount' => [
+            'title' => ts('Soft Credit Amount Stats'),
+            'name' => 'amount',
+            'statistics' => [
+              'count' => ts('Soft Credits'),
+              'sum' => ts('Soft Credit Aggregate'),
+              'avg' => ts('Soft Credit Avg'),
+            ],
+          ],
+        ],
+        'grouping' => 'contri-fields',
+        'filters' => [
+          'amount' => [
+            'title' => ts('Soft Credit Amount'),
+          ],
+          'soft_credit_type_id' => [
+            'title' => ts('Soft Credit Type'),
+            'operatorType' => CRM_Report_Form::OP_MULTISELECT,
+            'options' => CRM_Core_OptionGroup::values('soft_credit_type'),
+            'default' => NULL,
+            'type' => CRM_Utils_Type::T_STRING,
+          ],
+          'soft_sum' => [
+            'title' => ts('Soft Credit Aggregate'),
+            'type' => CRM_Report_Form::OP_INT,
+            'dbAlias' => 'civicrm_contribution_soft_soft_amount_sum',
+            'having' => TRUE,
+          ],
+          'soft_count' => [
+            'title' => ts('Soft Credits Count'),
+            'type' => CRM_Report_Form::OP_INT,
+            'dbAlias' => 'civicrm_contribution_soft_soft_amount_count',
+            'having' => TRUE,
+          ],
+          'soft_avg' => [
+            'title' => ts('Soft Credit Avg'),
+            'type' => CRM_Report_Form::OP_INT,
+            'dbAlias' => 'civicrm_contribution_soft_soft_amount_avg',
+            'having' => TRUE,
+          ],
+        ],
+      ],
+    ] + $this->addAddressFields();
+
+    $this->addCampaignFields('civicrm_contribution', TRUE);
+
+    // Add charts support
+    $this->_charts = [
+      '' => ts('Tabular'),
+      'barChart' => ts('Bar Chart'),
+      'pieChart' => ts('Pie Chart'),
+    ];
+
+    $this->_tagFilter = TRUE;
+    $this->_groupFilter = TRUE;
+    $this->_currencyColumn = 'civicrm_contribution_currency';
+    parent::__construct();
+  }
+
+  /**
+   * Set select clause.
+   */
+  public function select() {
+    $select = [];
+    $this->_columnHeaders = [];
+    foreach ($this->_columns as $tableName => $table) {
+      if (array_key_exists('group_bys', $table)) {
+        foreach ($table['group_bys'] as $fieldName => $field) {
+          if (!empty($this->_params['group_bys'][$fieldName])) {
+            switch (CRM_Utils_Array::value($fieldName, $this->_params['group_bys_freq'])) {
+              case 'YEARWEEK':
+                $select[] = "DATE_SUB({$field['dbAlias']}, INTERVAL WEEKDAY({$field['dbAlias']}) DAY) AS {$tableName}_{$fieldName}_start";
+                $select[] = "YEARWEEK({$field['dbAlias']}) AS {$tableName}_{$fieldName}_subtotal";
+                $select[] = "WEEKOFYEAR({$field['dbAlias']}) AS {$tableName}_{$fieldName}_interval";
+                $field['title'] = ts('Week Beginning');
+                break;
+
+              case 'YEAR':
+                $select[] = "MAKEDATE(YEAR({$field['dbAlias']}), 1)  AS {$tableName}_{$fieldName}_start";
+                $select[] = "YEAR({$field['dbAlias']}) AS {$tableName}_{$fieldName}_subtotal";
+                $select[] = "YEAR({$field['dbAlias']}) AS {$tableName}_{$fieldName}_interval";
+                $field['title'] = ts('Year Beginning');
+                break;
+
+              case 'FISCALYEAR':
+                $config = CRM_Core_Config::singleton();
+                $fy = $config->fiscalYearStart;
+                $fiscal = self::fiscalYearOffset($field['dbAlias']);
+
+                $select[] = "DATE_ADD(MAKEDATE({$fiscal}, 1), INTERVAL ({$fy['M']})-1 MONTH) AS {$tableName}_{$fieldName}_start";
+                $select[] = "{$fiscal} AS {$tableName}_{$fieldName}_subtotal";
+                $select[] = "{$fiscal} AS {$tableName}_{$fieldName}_interval";
+                $field['title'] = ts('Fiscal Year Beginning');
+                break;
+
+              case 'MONTH':
+                $select[] = "DATE_SUB({$field['dbAlias']}, INTERVAL (DAYOFMONTH({$field['dbAlias']})-1) DAY) as {$tableName}_{$fieldName}_start";
+                $select[] = "MONTH({$field['dbAlias']}) AS {$tableName}_{$fieldName}_subtotal";
+                $select[] = "MONTHNAME({$field['dbAlias']}) AS {$tableName}_{$fieldName}_interval";
+                $field['title'] = ts('Month Beginning');
+                break;
+
+              case 'DATE':
+                $select[] = "DATE({$field['dbAlias']}) as {$tableName}_{$fieldName}_start";
+                $field['title'] = ts('Date');
+                break;
+
+              case 'QUARTER':
+                $select[] = "STR_TO_DATE(CONCAT( 3 * QUARTER( {$field['dbAlias']} ) -2 , '/', '1', '/', YEAR( {$field['dbAlias']} ) ), '%m/%d/%Y') AS {$tableName}_{$fieldName}_start";
+                $select[] = "QUARTER({$field['dbAlias']}) AS {$tableName}_{$fieldName}_subtotal";
+                $select[] = "QUARTER({$field['dbAlias']}) AS {$tableName}_{$fieldName}_interval";
+                $field['title'] = 'Quarter';
+                break;
+            }
+            if (!empty($this->_params['group_bys_freq'][$fieldName])) {
+              $this->_interval = $this->_params['group_bys_freq'][$fieldName];
+              $this->_columnHeaders["{$tableName}_{$fieldName}_start"]['title'] = $field['title'];
+              $this->_columnHeaders["{$tableName}_{$fieldName}_start"]['type'] = $field['type'];
+              $this->_columnHeaders["{$tableName}_{$fieldName}_start"]['group_by'] = $this->_params['group_bys_freq'][$fieldName];
+
+              // just to make sure these values are transferred to rows.
+              // since we need that for calculation purpose,
+              // e.g making subtotals look nicer or graphs
+              $this->_columnHeaders["{$tableName}_{$fieldName}_interval"] = ['no_display' => TRUE];
+              $this->_columnHeaders["{$tableName}_{$fieldName}_subtotal"] = ['no_display' => TRUE];
+            }
+          }
+        }
+      }
+
+      if (array_key_exists('fields', $table)) {
+        foreach ($table['fields'] as $fieldName => $field) {
+          if (!empty($field['required']) ||
+            !empty($this->_params['fields'][$fieldName])
+          ) {
+            // only include statistics columns if set
+            if (!empty($field['statistics'])) {
+              foreach ($field['statistics'] as $stat => $label) {
+                $this->_columnHeaders["{$tableName}_{$fieldName}_{$stat}"]['title'] = $label;
+                $this->_columnHeaders["{$tableName}_{$fieldName}_{$stat}"]['type'] = $field['type'];
+                $this->_statFields[] = "{$tableName}_{$fieldName}_{$stat}";
+                switch (strtolower($stat)) {
+                  case 'sum':
+                    $select[] = "SUM(IF (contribution_civireport.contribution_status_id IN (7, 3), -1 * {$field['dbAlias']}, {$field['dbAlias']})) as {$tableName}_{$fieldName}_{$stat}";
+                    break;
+
+                  case 'count':
+                    $select[] = "COUNT({$field['dbAlias']}) as {$tableName}_{$fieldName}_{$stat}";
+                    $this->_columnHeaders["{$tableName}_{$fieldName}_{$stat}"]['type'] = CRM_Utils_Type::T_INT;
+                    break;
+
+                  case 'avg':
+                    $select[] = "ROUND(AVG({$field['dbAlias']}),2) as {$tableName}_{$fieldName}_{$stat}";
+                    break;
+                }
+              }
+            }
+            else {
+              $select[] = "{$field['dbAlias']} as {$tableName}_{$fieldName}";
+              $this->_columnHeaders["{$tableName}_{$fieldName}"]['type'] = $field['type'] ?? NULL;
+              $this->_columnHeaders["{$tableName}_{$fieldName}"]['title'] = $field['title'] ?? NULL;
+            }
+          }
+        }
+      }
+    }
+
+    $this->_selectClauses = $select;
+    $this->_select = "SELECT " . implode(', ', $select) . " ";
+  }
+
+  /**
+   * Set form rules.
+   *
+   * @param array $fields
+   * @param array $files
+   * @param CRM_Report_Form_Contribute_Summary $self
+   *
+   * @return array
+   */
+  public static function formRule($fields, $files, $self) {
+    // Check for searching combination of display columns and
+    // grouping criteria
+    $ignoreFields = ['total_amount', 'sort_name'];
+    $errors = $self->customDataFormRule($fields, $ignoreFields);
+
+    if (empty($fields['fields']['total_amount'])) {
+      foreach ([
+        'total_count_value',
+        'total_sum_value',
+        'total_avg_value',
+      ] as $val) {
+        if (!empty($fields[$val])) {
+          $errors[$val] = ts("Please select the Amount Statistics");
+        }
+      }
+    }
+
+    return $errors;
+  }
+
+  /**
+   * Set from clause.
+   *
+   * @param string $entity
+   *
+   * @todo fix function signature to match parent. Remove hacky passing of $entity
+   * to acheive unclear results.
+   */
+  public function from($entity = NULL) {
+    $softCreditJoinType = "LEFT";
+    if (!empty($this->_params['fields']['soft_amount']) &&
+      empty($this->_params['fields']['total_amount'])
+    ) {
+      // if its only soft credit stats, use inner join
+      $softCreditJoinType = "INNER";
+    }
+
+    $softCreditJoin = "{$softCreditJoinType} JOIN civicrm_contribution_soft {$this->_aliases['civicrm_contribution_soft']}
+                       ON {$this->_aliases['civicrm_contribution_soft']}.contribution_id = {$this->_aliases['civicrm_contribution']}.id";
+    if ($entity == 'contribution' || empty($this->_params['fields']['soft_amount'])) {
+      $softCreditJoin .= " AND {$this->_aliases['civicrm_contribution_soft']}.id = (SELECT MIN(id) FROM civicrm_contribution_soft cs WHERE cs.contribution_id = {$this->_aliases['civicrm_contribution']}.id) ";
+    }
+
+    $this->setFromBase('civicrm_contact');
+
+    $this->_from .= "
+             INNER JOIN civicrm_contribution   {$this->_aliases['civicrm_contribution']}
+                     ON {$this->_aliases['civicrm_contact']}.id = {$this->_aliases['civicrm_contribution']}.contact_id AND
+                        {$this->_aliases['civicrm_contribution']}.is_test = 0 AND
+                        {$this->_aliases['civicrm_contribution']}.is_template = 0
+             {$softCreditJoin}
+             LEFT  JOIN civicrm_financial_type  {$this->_aliases['civicrm_financial_type']}
+                     ON {$this->_aliases['civicrm_contribution']}.financial_type_id ={$this->_aliases['civicrm_financial_type']}.id
+             ";
+
+    $this->joinAddressFromContact();
+    $this->joinPhoneFromContact();
+    $this->joinEmailFromContact();
+
+    //for contribution batches
+    if ($this->isTableSelected('civicrm_batch')) {
+      $this->_from .= "
+        LEFT JOIN civicrm_entity_financial_trxn eft
+          ON eft.entity_id = {$this->_aliases['civicrm_contribution']}.id AND
+            eft.entity_table = 'civicrm_contribution'
+        LEFT JOIN civicrm_entity_batch {$this->_aliases['civicrm_batch']}
+          ON ({$this->_aliases['civicrm_batch']}.entity_id = eft.financial_trxn_id
+          AND {$this->_aliases['civicrm_batch']}.entity_table = 'civicrm_financial_trxn')";
+    }
+
+    $this->addFinancialTrxnFromClause();
+  }
+
+  /**
+   * Set group by clause.
+   */
+  public function groupBy() {
+    parent::groupBy();
+
+    $isGroupByFrequency = !empty($this->_params['group_bys_freq']);
+
+    if (!empty($this->_params['group_bys']) &&
+      is_array($this->_params['group_bys'])
+    ) {
+
+      if (!empty($this->_statFields) &&
+        (($isGroupByFrequency && count($this->_groupByArray) <= 1) || (!$isGroupByFrequency)) &&
+        !$this->_having
+      ) {
+        $this->_rollup = " WITH ROLLUP";
+      }
+      $groupBy = [];
+      foreach ($this->_groupByArray as $key => $val) {
+        if (strpos($val, ';;') !== FALSE) {
+          $groupBy = array_merge($groupBy, explode(';;', $val));
+        }
+        else {
+          $groupBy[] = $this->_groupByArray[$key];
+        }
+      }
+      $this->_groupBy = "GROUP BY " . implode(', ', $groupBy);
+    }
+    else {
+      $this->_groupBy = "GROUP BY {$this->_aliases['civicrm_contact']}.id";
+    }
+    $this->_groupBy .= $this->_rollup;
+  }
+
+  /**
+   * Store having clauses as an array.
+   */
+  public function storeWhereHavingClauseArray() {
+    parent::storeWhereHavingClauseArray();
+    if (empty($this->_params['fields']['soft_amount']) &&
+      !empty($this->_havingClauses)
+    ) {
+      foreach ($this->_havingClauses as $key => $havingClause) {
+        if (stristr($havingClause, 'soft_soft')) {
+          unset($this->_havingClauses[$key]);
+        }
+      }
+    }
+  }
+
+  /**
+   * Set statistics.
+   *
+   * @param array $rows
+   *
+   * @return array
+   *
+   * @throws \CRM_Core_Exception
+   */
+  public function statistics(&$rows) {
+    $statistics = parent::statistics($rows);
+
+    $softCredit = $this->_params['fields']['soft_amount'] ?? NULL;
+    $onlySoftCredit = $softCredit && !CRM_Utils_Array::value('total_amount', $this->_params['fields']);
+    if (!isset($this->_groupByArray['civicrm_contribution_currency'])) {
+      $this->_groupByArray['civicrm_contribution_currency'] = 'currency';
+    }
+    $group = ' GROUP BY ' . implode(', ', $this->_groupByArray);
+
+    $this->from('contribution');
+    if ($softCredit) {
+      $this->from();
+    }
+    $this->customDataFrom();
+
+    // Ensure that Extensions that modify the from statement in the sql also modify it in the statistics.
+    CRM_Utils_Hook::alterReportVar('sql', $this, $this);
+
+    $contriQuery = "
+      COUNT( {$this->_aliases['civicrm_contribution']}.total_amount )        as civicrm_contribution_total_amount_count,
+      SUM( IF (contribution_civireport.contribution_status_id IN (7, 3), -1 * {$this->_aliases['civicrm_contribution']}.total_amount,  {$this->_aliases['civicrm_contribution']}.total_amount ) )          as civicrm_contribution_total_amount_sum,
+      ROUND((IF (contribution_civireport.contribution_status_id IN (7, 3), -1 * {$this->_aliases['civicrm_contribution']}.total_amount, {$this->_aliases['civicrm_contribution']}.total_amount)/ COUNT( {$this->_aliases['civicrm_contribution']}.total_amount )), 2) as civicrm_contribution_total_amount_avg,
+      {$this->_aliases['civicrm_contribution']}.currency                    as currency
+      {$this->_from} {$this->_where}
+    ";
+
+    if ($softCredit) {
+      $selectOnlySoftCredit = "
+        COUNT({$this->_aliases['civicrm_contribution_soft']}.amount )        as civicrm_contribution_soft_soft_amount_count,
+        SUM({$this->_aliases['civicrm_contribution_soft']}.amount )          as civicrm_contribution_soft_soft_amount_sum,
+        ROUND(AVG({$this->_aliases['civicrm_contribution_soft']}.amount), 2) as civicrm_contribution_soft_soft_amount_avg,
+      ";
+
+      $selectWithSoftCredit = "
+        COUNT({$this->_aliases['civicrm_contribution_soft']}.amount )        as civicrm_contribution_soft_soft_amount_count,
+        SUM({$this->_aliases['civicrm_contribution_soft']}.amount )          as civicrm_contribution_soft_soft_amount_sum,
+        ROUND(AVG({$this->_aliases['civicrm_contribution_soft']}.amount), 2) as civicrm_contribution_soft_soft_amount_avg,
+        COUNT({$this->_aliases['civicrm_contribution']}.total_amount )        as civicrm_contribution_total_amount_count,
+        SUM({$this->_aliases['civicrm_contribution']}.total_amount )          as civicrm_contribution_total_amount_sum,
+        ROUND(AVG({$this->_aliases['civicrm_contribution']}.total_amount), 2) as civicrm_contribution_total_amount_avg,
+      ";
+
+      if ($softCredit && $onlySoftCredit) {
+        $contriQuery = "{$selectOnlySoftCredit} {$contriQuery}";
+        $softSQL = "SELECT {$selectOnlySoftCredit} {$this->_aliases['civicrm_contribution']}.currency as currency
+        {$this->_from} {$this->_where} {$group} {$this->_having}";
+      }
+      elseif ($softCredit && !$onlySoftCredit) {
+        $contriQuery = "{$selectOnlySoftCredit} {$contriQuery}";
+        $softSQL = "SELECT {$selectWithSoftCredit} {$this->_aliases['civicrm_contribution']}.currency as currency
+        {$this->_from} {$this->_where} {$group} {$this->_having}";
+      }
+    }
+
+    $contriSQL = "SELECT {$contriQuery} {$group} {$this->_having}";
+    $contriDAO = CRM_Core_DAO::executeQuery($contriSQL);
+    $this->addToDeveloperTab($contriSQL);
+    $currencies = $currAmount = $currAverage = $currCount = [];
+    $totalAmount = $average = $mode = $median = [];
+    $softTotalAmount = $softAverage = $averageCount = $averageSoftCount = [];
+    $softCount = $count = 0;
+    while ($contriDAO->fetch()) {
+      if (!isset($currAmount[$contriDAO->currency])) {
+        $currAmount[$contriDAO->currency] = 0;
+      }
+      if (!isset($currCount[$contriDAO->currency])) {
+        $currCount[$contriDAO->currency] = 0;
+      }
+      if (!isset($currAverage[$contriDAO->currency])) {
+        $currAverage[$contriDAO->currency] = 0;
+      }
+      if (!isset($averageCount[$contriDAO->currency])) {
+        $averageCount[$contriDAO->currency] = 0;
+      }
+      $currAmount[$contriDAO->currency] += $contriDAO->civicrm_contribution_total_amount_sum;
+      $currCount[$contriDAO->currency] += $contriDAO->civicrm_contribution_total_amount_count;
+      $currAverage[$contriDAO->currency] += $contriDAO->civicrm_contribution_total_amount_avg;
+      $averageCount[$contriDAO->currency]++;
+      $count += $contriDAO->civicrm_contribution_total_amount_count;
+
+      if (!in_array($contriDAO->currency, $currencies)) {
+        $currencies[] = $contriDAO->currency;
+      }
+    }
+
+    foreach ($currencies as $currency) {
+      $totalAmount[] = CRM_Utils_Money::format($currAmount[$currency], $currency) .
+        " (" . $currCount[$currency] . ")";
+      $average[] = CRM_Utils_Money::format(round(($currAmount[$currency] / $currCount[$currency]), 2), $currency);
+    //  $average[] = CRM_Utils_Money::format(($currAverage[$currency] / $averageCount[$currency]), $currency);
+    }
+
+    $groupBy = "\n{$group}, {$this->_aliases['civicrm_contribution']}.total_amount";
+    $orderBy = "\nORDER BY civicrm_contribution_total_amount_count DESC";
+    $modeSQL = "SELECT MAX(civicrm_contribution_total_amount_count) as civicrm_contribution_total_amount_count,
+      SUBSTRING_INDEX(GROUP_CONCAT(amount ORDER BY mode.civicrm_contribution_total_amount_count DESC SEPARATOR ';'), ';', 1) as amount,
+      currency
+      FROM (SELECT {$this->_aliases['civicrm_contribution']}.total_amount as amount,
+    {$contriQuery} {$groupBy} {$orderBy}) as mode GROUP BY currency";
+
+    $mode = $this->calculateMode($modeSQL);
+    $median = $this->calculateMedian();
+
+    $currencies = $currSoftAmount = $currSoftAverage = $currSoftCount = [];
+    if ($softCredit) {
+      $softDAO = CRM_Core_DAO::executeQuery($softSQL);
+      $this->addToDeveloperTab($softSQL);
+      while ($softDAO->fetch()) {
+        if (!isset($currSoftAmount[$softDAO->currency])) {
+          $currSoftAmount[$softDAO->currency] = 0;
+        }
+        if (!isset($currSoftCount[$softDAO->currency])) {
+          $currSoftCount[$softDAO->currency] = 0;
+        }
+        if (!isset($currSoftAverage[$softDAO->currency])) {
+          $currSoftAverage[$softDAO->currency] = 0;
+        }
+        if (!isset($averageSoftCount[$softDAO->currency])) {
+          $averageSoftCount[$softDAO->currency] = 0;
+        }
+        $currSoftAmount[$softDAO->currency] += $softDAO->civicrm_contribution_soft_soft_amount_sum;
+        $currSoftCount[$softDAO->currency] += $softDAO->civicrm_contribution_soft_soft_amount_count;
+        $currSoftAverage[$softDAO->currency] += $softDAO->civicrm_contribution_soft_soft_amount_avg;
+        $averageSoftCount[$softDAO->currency]++;
+        $softCount += $softDAO->civicrm_contribution_soft_soft_amount_count;
+
+        if (!in_array($softDAO->currency, $currencies)) {
+          $currencies[] = $softDAO->currency;
+        }
+      }
+
+      foreach ($currencies as $currency) {
+        $softTotalAmount[] = CRM_Utils_Money::format($currSoftAmount[$currency], $currency) .
+          " (" . $currSoftCount[$currency] . ")";
+        $softAverage[] = CRM_Utils_Money::format(($currSoftAverage[$currency] / $averageSoftCount[$currency]), $currency);
+      }
+    }
+
+    if (!$onlySoftCredit) {
+      $statistics['counts']['amount'] = [
+        'title' => ts('Total Amount'),
+        'value' => implode(',  ', $totalAmount),
+        'type' => CRM_Utils_Type::T_STRING,
+      ];
+      $statistics['counts']['count'] = [
+        'title' => ts('Total Contributions'),
+        'value' => $count,
+      ];
+      $statistics['counts']['avg'] = [
+        'title' => ts('Average'),
+        'value' => implode(',  ', $average),
+        'type' => CRM_Utils_Type::T_STRING,
+      ];
+      $statistics['counts']['mode'] = [
+        'title' => ts('Mode'),
+        'value' => implode(',  ', $mode),
+        'type' => CRM_Utils_Type::T_STRING,
+      ];
+      $statistics['counts']['median'] = [
+        'title' => ts('Median'),
+        'value' => implode(',  ', $median),
+        'type' => CRM_Utils_Type::T_STRING,
+      ];
+    }
+    if ($softCredit) {
+      $statistics['counts']['soft_amount'] = [
+        'title' => ts('Total Soft Credit Amount'),
+        'value' => implode(',  ', $softTotalAmount),
+        'type' => CRM_Utils_Type::T_STRING,
+      ];
+      $statistics['counts']['soft_count'] = [
+        'title' => ts('Total Soft Credits'),
+        'value' => $softCount,
+      ];
+      $statistics['counts']['soft_avg'] = [
+        'title' => ts('Average Soft Credit'),
+        'value' => implode(',  ', $softAverage),
+        'type' => CRM_Utils_Type::T_STRING,
+      ];
+    }
+    return $statistics;
+  }
+
+  /**
+   * Build chart.
+   *
+   * @param array $original_rows
+   */
+  public function buildChart(&$original_rows) {
+    $graphRows = [];
+
+    if (!empty($this->_params['charts'])) {
+      if (!empty($this->_params['group_bys']['receive_date'])) {
+
+        $contrib = !empty($this->_params['fields']['total_amount']);
+        $softContrib = !empty($this->_params['fields']['soft_amount']);
+
+        // Make a copy so that we don't affect what gets passed later to hooks etc.
+        $rows = $original_rows;
+        if ($this->_rollup) {
+          // Remove the total row otherwise it overwrites the real last month's data since it has the
+          // same date.
+          array_pop($rows);
+        }
+
+        foreach ($rows as $key => $row) {
+          if ($row['civicrm_contribution_receive_date_subtotal']) {
+            $graphRows['receive_date'][] = $row['civicrm_contribution_receive_date_start'];
+            $graphRows[$this->_interval][] = $row['civicrm_contribution_receive_date_interval'];
+            if ($softContrib && $contrib) {
+              // both contri & soft contri stats are present
+              $graphRows['multiValue'][0][] = $row['civicrm_contribution_total_amount_sum'];
+              $graphRows['multiValue'][1][] = $row['civicrm_contribution_soft_soft_amount_sum'];
+            }
+            elseif ($softContrib) {
+              // only soft contributions
+              $graphRows['multiValue'][0][] = $row['civicrm_contribution_soft_soft_amount_sum'];
+            }
+            else {
+              // only contributions
+              $graphRows['multiValue'][0][] = $row['civicrm_contribution_total_amount_sum'];
+            }
+          }
+        }
+
+        if ($softContrib && $contrib) {
+          $graphRows['barKeys'][0] = ts('Contributions');
+          $graphRows['barKeys'][1] = ts('Soft Credits');
+          $graphRows['legend'] = ts('Contributions and Soft Credits');
+        }
+        elseif ($softContrib) {
+          $graphRows['legend'] = ts('Soft Credits');
+        }
+
+        // build the chart.
+        $config = CRM_Core_Config::Singleton();
+        $graphRows['xname'] = $this->_interval;
+        $graphRows['yname'] = ts('Amount (%1)', [1 => $config->defaultCurrency]);
+        CRM_Utils_Chart::chart($graphRows, $this->_params['charts'], $this->_interval);
+        $this->assign('chartType', $this->_params['charts']);
+      }
+    }
+  }
+
+  /**
+   * Alter display of rows.
+   *
+   * Iterate through the rows retrieved via SQL and make changes for display purposes,
+   * such as rendering contacts as links.
+   *
+   * @param array $rows
+   *   Rows generated by SQL, with an array for each row.
+   */
+  public function alterDisplay(&$rows) {
+    $entryFound = FALSE;
+    $contributionStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'label');
+    $contributionPages = CRM_Contribute_PseudoConstant::contributionPage();
+    //CRM-16338 if both soft-credit and contribution are enabled then process the contribution's
+    //total amount's average, count and sum separately and add it to the respective result list
+    $softCredit = (!empty($this->_params['fields']['soft_amount']) && !empty($this->_params['fields']['total_amount']));
+    if ($softCredit) {
+      $this->from('contribution');
+      $this->customDataFrom();
+      $contriSQL = "{$this->_select} {$this->_from} {$this->_where} {$this->_groupBy} {$this->_having} {$this->_orderBy} {$this->_limit}";
+      CRM_Core_DAO::disableFullGroupByMode();
+      $contriDAO = CRM_Core_DAO::executeQuery($contriSQL);
+      CRM_Core_DAO::reenableFullGroupByMode();
+      $this->addToDeveloperTab($contriSQL);
+      $contriFields = [
+        'civicrm_contribution_total_amount_sum',
+        'civicrm_contribution_total_amount_avg',
+        'civicrm_contribution_total_amount_count',
+      ];
+      $count = 0;
+      while ($contriDAO->fetch()) {
+        foreach ($contriFields as $column) {
+          $rows[$count][$column] = $contriDAO->$column;
+        }
+        $count++;
+      }
+    }
+    foreach ($rows as $rowNum => $row) {
+      // make count columns point to detail report
+      if (!empty($this->_params['group_bys']['receive_date']) &&
+        !empty($row['civicrm_contribution_receive_date_start']) &&
+        CRM_Utils_Array::value('civicrm_contribution_receive_date_start', $row) &&
+        !empty($row['civicrm_contribution_receive_date_subtotal'])
+      ) {
+
+        $dateStart = CRM_Utils_Date::customFormat($row['civicrm_contribution_receive_date_start'], '%Y%m%d');
+        $endDate = new DateTime($dateStart);
+        $dateEnd = [];
+
+        list($dateEnd['Y'], $dateEnd['M'], $dateEnd['d']) = explode(':', $endDate->format('Y:m:d'));
+
+        switch (strtolower($this->_params['group_bys_freq']['receive_date'])) {
+          case 'month':
+            $dateEnd = date("Ymd", mktime(0, 0, 0, $dateEnd['M'] + 1,
+              $dateEnd['d'] - 1, $dateEnd['Y']
+            ));
+            break;
+
+          case 'year':
+            $dateEnd = date("Ymd", mktime(0, 0, 0, $dateEnd['M'],
+              $dateEnd['d'] - 1, $dateEnd['Y'] + 1
+            ));
+            break;
+
+          case 'fiscalyear':
+            $dateEnd = date("Ymd", mktime(0, 0, 0, $dateEnd['M'],
+              $dateEnd['d'] - 1, $dateEnd['Y'] + 1
+            ));
+            break;
+
+          case 'yearweek':
+            $dateEnd = date("Ymd", mktime(0, 0, 0, $dateEnd['M'],
+              $dateEnd['d'] + 6, $dateEnd['Y']
+            ));
+            break;
+
+          case 'quarter':
+            $dateEnd = date("Ymd", mktime(0, 0, 0, $dateEnd['M'] + 3,
+              $dateEnd['d'] - 1, $dateEnd['Y']
+            ));
+            break;
+        }
+        $url = CRM_Report_Utils_Report::getNextUrl('contribute/detail',
+          "reset=1&force=1&receive_date_from={$dateStart}&receive_date_to={$dateEnd}",
+          $this->_absoluteUrl,
+          $this->_id,
+          $this->_drilldownReport
+        );
+        $rows[$rowNum]['civicrm_contribution_receive_date_start_link'] = $url;
+        $rows[$rowNum]['civicrm_contribution_receive_date_start_hover'] = ts('List all contribution(s) for this date unit.');
+        $entryFound = TRUE;
+      }
+
+      // make subtotals look nicer
+      if (array_key_exists('civicrm_contribution_receive_date_subtotal', $row) &&
+        !$row['civicrm_contribution_receive_date_subtotal']
+      ) {
+        $this->fixSubTotalDisplay($rows[$rowNum], $this->_statFields);
+        $entryFound = TRUE;
+      }
+
+      // convert display name to links
+      if (array_key_exists('civicrm_contact_sort_name', $row) &&
+        array_key_exists('civicrm_contact_id', $row)
+      ) {
+        $url = CRM_Report_Utils_Report::getNextUrl('contribute/detail',
+          'reset=1&force=1&id_op=eq&id_value=' . $row['civicrm_contact_id'],
+          $this->_absoluteUrl, $this->_id, $this->_drilldownReport
+        );
+        $rows[$rowNum]['civicrm_contact_sort_name_link'] = $url;
+        $rows[$rowNum]['civicrm_contact_sort_name_hover'] = ts("Lists detailed contribution(s) for this record.");
+        $entryFound = TRUE;
+      }
+
+      // convert contribution status id to status name
+      if ($value = CRM_Utils_Array::value('civicrm_contribution_contribution_status_id', $row)) {
+        $rows[$rowNum]['civicrm_contribution_contribution_status_id'] = $contributionStatus[$value];
+        $entryFound = TRUE;
+      }
+
+      if (!empty($row['civicrm_financial_trxn_card_type_id'])) {
+        $rows[$rowNum]['civicrm_financial_trxn_card_type_id'] = $this->getLabels($row['civicrm_financial_trxn_card_type_id'], 'CRM_Financial_DAO_FinancialTrxn', 'card_type_id');
+        $entryFound = TRUE;
+      }
+
+      if ($value = CRM_Utils_Array::value('civicrm_contribution_contribution_page_id', $row)) {
+        $rows[$rowNum]['civicrm_contribution_contribution_page_id'] = $contributionPages[$value];
+        $entryFound = TRUE;
+      }
+
+      // If using campaigns, convert campaign_id to campaign title
+      if (array_key_exists('civicrm_contribution_campaign_id', $row)) {
+        if ($value = $row['civicrm_contribution_campaign_id']) {
+          $rows[$rowNum]['civicrm_contribution_campaign_id'] = $this->campaigns[$value];
+        }
+        $entryFound = TRUE;
+      }
+
+      // convert batch id to batch title
+      if (!empty($row['civicrm_batch_batch_id']) && !in_array('Subtotal', $rows[$rowNum])) {
+        $rows[$rowNum]['civicrm_batch_batch_id'] = $this->getLabels($row['civicrm_batch_batch_id'], 'CRM_Batch_BAO_EntityBatch', 'batch_id');
+        $entryFound = TRUE;
+      }
+
+      $entryFound = $this->alterDisplayAddressFields($row, $rows, $rowNum, 'contribute/detail', 'List all contribution(s) for this ') ? TRUE : $entryFound;
+      $entryFound = $this->alterDisplayContactFields($row, $rows, $rowNum, 'contribute/detail', 'List all contribution(s) for this ') ? TRUE : $entryFound;
+
+      // skip looking further in rows, if first row itself doesn't
+      // have the column we need
+      if (!$entryFound) {
+        break;
+      }
+    }
+  }
+
+  /**
+   * Calculate mode.
+   *
+   * Note this is a slow query. Alternative is extended reports.
+   *
+   * @param string $sql
+   * @return array|null
+   */
+  protected function calculateMode($sql) {
+    $mode = [];
+    $modeDAO = CRM_Core_DAO::executeQuery($sql);
+    while ($modeDAO->fetch()) {
+      if ($modeDAO->civicrm_contribution_total_amount_count > 1) {
+        $mode[] = CRM_Utils_Money::format($modeDAO->amount, $modeDAO->currency);
+      }
+      else {
+        $mode[] = ts('N/A');
+      }
+    }
+    return $mode;
+  }
+
+  /**
+   * Calculate mode.
+   *
+   * Note this is a slow query. Alternative is extended reports.
+   *
+   * @return array|null
+   */
+  protected function calculateMedian() {
+    $sql = "{$this->_from} {$this->_where}";
+    $currencies = CRM_Core_OptionGroup::values('currencies_enabled');
+    $median = [];
+    foreach ($currencies as $currency => $val) {
+      $midValue = 0;
+      $where = "AND {$this->_aliases['civicrm_contribution']}.currency = '{$currency}'";
+      $rowCount = CRM_Core_DAO::singleValueQuery("SELECT count(*) as count {$sql} {$where}");
+
+      $even = FALSE;
+      $offset = 1;
+      $medianRow = floor($rowCount / 2);
+      if ($rowCount % 2 == 0 && !empty($medianRow)) {
+        $even = TRUE;
+        $offset++;
+        $medianRow--;
+      }
+
+      $medianValue = "SELECT {$this->_aliases['civicrm_contribution']}.total_amount as median
+             {$sql} {$where}
+             ORDER BY median LIMIT {$medianRow},{$offset}";
+      $medianValDAO = CRM_Core_DAO::executeQuery($medianValue);
+      while ($medianValDAO->fetch()) {
+        if ($even) {
+          $midValue = $midValue + $medianValDAO->median;
+        }
+        else {
+          $median[] = CRM_Utils_Money::format($medianValDAO->median, $currency);
+        }
+      }
+      if ($even) {
+        $midValue = $midValue / 2;
+        $median[] = CRM_Utils_Money::format($midValue, $currency);
+      }
+    }
+    return $median;
+  }
+
+/**
+ * Add Financial Transaction into From Table if required.
+ */
+public function addFinancialTrxnFromClause() {
+  if ($this->isTableSelected('civicrm_financial_trxn')) {
+    $this->_from .= "
+       INNER JOIN civicrm_entity_financial_trxn eftcc
+         ON ({$this->_aliases['civicrm_contribution']}.id = eftcc.entity_id AND
+           eftcc.entity_table = 'civicrm_contribution')
+       INNER JOIN civicrm_financial_trxn {$this->_aliases['civicrm_financial_trxn']}
+         ON {$this->_aliases['civicrm_financial_trxn']}.id = eftcc.financial_trxn_id AND {$this->_aliases['civicrm_financial_trxn']}.is_payment = 1 AND status_id = {$this->_aliases['civicrm_contribution']}.contribution_status_id \n";
+  }
+}
+
+}
diff --git a/acop.php b/acop.php
index 59c35d0e14769515616b49c89235ffee567ac751..d3b59fb9f37ad37f3e4cf25163421f978674b07d 100644
--- a/acop.php
+++ b/acop.php
@@ -144,22 +144,23 @@ function acop_civicrm_themes(&$themes) {
 }
 
 function acop_civicrm_alterReportVar($type, &$columns, &$form) {
-  if ('CRM_Report_Form_Contribute_Summary' == get_class($form) && $type == 'sql') {
+if ('CRM_Report_Form_Contribute_Summary' == get_class($form) && $type == 'sql') {
     $selects = $columns->_selectClauses;
     foreach ($selects as &$clause) {
       if ($clause == "SUM(contribution_civireport.total_amount) as civicrm_contribution_total_amount_sum") {
-        $clause = "SUM(IF (contribution_civireport.contribution_status_id = 7, -1 * contribution_civireport.total_amount, contribution_civireport.total_amount)) as civicrm_contribution_total_amount_sum";
+        $clause = "SUM(IF (contribution_civireport.contribution_status_id IN (7, 3), -1 * contribution_civireport.total_amount, contribution_civireport.total_amount)) as civicrm_contribution_total_amount_sum";
       }
       if ($clause == "ROUND(AVG(contribution_civireport.total_amount),2) as civicrm_contribution_total_amount_avg") {
-        $clause = "ROUND(AVG(IF (contribution_civireport.contribution_status_id = 7, -1 * contribution_civireport.total_amount, contribution_civireport.total_amount)),2) as civicrm_contribution_total_amount_avg";
+        $clause = "ROUND(AVG(IF (contribution_civireport.contribution_status_id IN (7, 3), -1 * contribution_civireport.total_amount, contribution_civireport.total_amount)),2) as civicrm_contribution_total_amount_avg";
       }
       if ($clause == "contribution_civireport.non_deductible_amount as civicrm_contribution_non_deductible_amount") {
-        $clause = "IF (contribution_civireport.contribution_status_id = 7, -1 * contribution_civireport.non_deductible_amount, contribution_civireport.non_deductible_amount) as civicrm_contribution_non_deductible_amount";
+        $clause = "IF (contribution_civireport.contribution_status_id IN (7, 3), -1 * contribution_civireport.non_deductible_amount, contribution_civireport.non_deductible_amount) as civicrm_contribution_non_deductible_amount";
       }
     }
     $columns->_selectClauses = $selects;
     $columns->_select = "SELECT SQL_CALC_FOUND_ROWS " . implode(', ', $selects);
   }
+
   if ('CRM_Report_Form_Contribute_Detail' == get_class($form) && $type == 'sql') {
     $selects = $columns->_selectClauses;
     foreach ($selects as &$clause) {
@@ -174,6 +175,7 @@ function acop_civicrm_alterReportVar($type, &$columns, &$form) {
     }
   }
   if ('CRM_Report_Form_Contribute_Summary' == get_class($form) && $type == 'columns') {
+    $columns['civicrm_contribution']['fields']['total_amount']['type'] = CRM_Utils_Type::T_INT;
     $columns['civicrm_contribution']['filters']['payment_instrument_id'] = [
       'title' => ts('Payment Method'),
       'operatorType' => CRM_Report_Form::OP_MULTISELECT,
@@ -235,6 +237,25 @@ function acop_civicrm_validateForm($formName, &$fields, &$files, &$form, &$error
  * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_buildForm/
  */
 function acop_civicrm_buildForm($formName, &$form) {
+  if ($formName === 'CRM_Contribute_Form_Contribution_Main') {
+    /*CRM_Core_Resources::singleton()->addScript(
+      "CRM.$(function($) {
+        $('#_qf_Main_upload-bottom, #is_for_organization').on('click', function(e) {
+          if ($('#is_for_organization').length && $('#is_for_organization').is(':checked') === false ) {
+            $('#on-behalf-block input').not('input[type=checkbox], input[type=radio], #onbehalfof_id').val('');
+            // clear checkboxes and radio
+            $('#on-behalf-block')
+                    .find('input[type=checkbox], input[type=radio]')
+                    .not('input[name=org_option]')
+                    .attr('checked', false);
+          }
+        });
+      });
+    ");*/
+	  /*if (!empty($form->_submitValues)) {
+		  CRM_Core_Error::debug_var('formsubmission', $form->_submitValues);
+  }*/
+  }
   if ($formName === 'CRM_Contribute_Form_Contribution_Main' && $form->getVar('_id') == '2') {
     Civi::resources()->addScriptFile('biz.jmaconsulting.acop', 'js/acop_membership_form.js');
     // Set future date for renewals.
@@ -268,7 +289,7 @@ function acop_civicrm_buildForm($formName, &$form) {
     if ($form->_action & CRM_Core_Action::ADD) {
       $totalItems = $form->getVar('_batchInfo')['item_count'];
       for ($i = 1; $i <= $totalItems; $i++) {
-        $form->setDefaults(["field[$i][custom_83]" => 0]);	    
+        $form->setDefaults(["field[$i][custom_83]" => 0]);
       }
       CRM_Core_Resources::singleton()->addScript(
         "CRM.$(function($) {
@@ -306,12 +327,57 @@ function acop_cdntaxreceipts_alter_receipt(&$receipt) {
       'contact_id_a' => $receipt['contact_id'],
       'relationship_type_id' => "Spouse of",
       'is_active' => 1,
+      'options' => ['limit' => 1],
     ])['values'];
+
+    if (empty($relationships)) {
+      $relationships = civicrm_api3('Relationship', 'get', [
+        'sequential' => 1,
+        'contact_id_b' => $receipt['contact_id'],
+        'relationship_type_id' => "Spouse of",
+	'is_active' => 1,
+	'options' => ['limit' => 1],
+      ])['values'];
+      $relContact = $relationships[0]['contact_id_a'];
+    }
+    else {
+      $relContact = $relationships[0]['contact_id_b'];
+    }
+    
     $spouseRecords[] = CRM_Contact_BAO_Contact::displayName($receipt['contact_id']);
-    foreach ($relationships as $relationship) {
-      $spouseRecords[] = CRM_Contact_BAO_Contact::displayName($relationship['contact_id_b']);
+    if (!empty($relContact)) {
+      $spouseRecords[] = CRM_Contact_BAO_Contact::displayName($relContact);
+      $receipt['display_name'] = implode(" and ", $spouseRecords);
     }
-    $receipt['display_name'] = implode(" and ", $spouseRecords);
+  }
+}
+
+/**
+ * Implements hook_civicrm_postProcess().
+ *
+ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postProcess
+ */
+function acop_civicrm_postProcess($formName, $form) {
+  if ($formName === 'CRM_Contribute_Form_Contribution_Confirm' || $formName === 'CRM_Contribute_Form_Contribution') {
+    return;
+    if ($formName === 'CRM_Contribute_Form_Contribution' && $form->getAction() !== CRM_Core_Action::ADD) {
+      return;
+    }
+    $params = $form->getVar('_params');
+    $logValues = [
+      'first_name' => $params['billing_first_name'], 
+      'last_name' => $params['billing_last_name'], 
+      'pan_truncation' => $params['pan_truncation'],
+      'street_address' => $params['street_address-5'],
+      'city' => $params['city-5'],
+      'postcode' => $params['postal_code-5'],
+      'logged_in_user_id' => CRM_Core_Session::getLoggedInContactID(),
+      'trigger_log_id' => CRM_Core_DAO::singleValueQuery('SELECT @civicrm_user_id'),
+      'amount' => $params['total_amount'], 
+      'trxn_id' => $params['trxn_id'],
+      'invoice_id' => $params['invoice_id'],
+    ];
+    Civi::log()->debug('{formName} was submitted and the following logged values {logValues} were captured', ['formName' => $formName, 'logValues' => $logValues]);
   }
 }