Commit 85aa92fa authored by Mathieu Lutfy's avatar Mathieu Lutfy Committed by Aegir user

infrastructure/ops#856 Add accountsync extension (for civixero)

parent 934c7e0f
<?php
class CRM_Accountsync_BAO_AccountContact extends CRM_Accountsync_DAO_AccountContact {
/**
* Create a new AccountContact based on array-data.
*
* @param array $params key-value pairs
*
* @return CRM_Accountsync_DAO_AccountContact|NULL
*/
public static function create($params) {
$className = 'CRM_Accountsync_DAO_AccountContact';
$entityName = 'AccountContact';
$hook = empty($params['id']) ? 'create' : 'edit';
CRM_Utils_Hook::pre($hook, $entityName, CRM_Utils_Array::value('id', $params), $params);
$instance = new $className();
$instance->copyValues($params);
$instance->save();
CRM_Utils_Hook::post($hook, $entityName, $instance->id, $instance);
return $instance;
}
}
<?php
class CRM_Accountsync_BAO_AccountInvoice extends CRM_Accountsync_DAO_AccountInvoice {
/**
* Create a new AccountInvoice based on array-data.
*
* @param array $params key-value pairs
*
* @return CRM_Accountsync_DAO_AccountInvoice|NULL
*/
public static function create($params) {
$className = 'CRM_Accountsync_DAO_AccountInvoice';
$entityName = 'AccountInvoice';
$hook = empty($params['id']) ? 'create' : 'edit';
CRM_Utils_Hook::pre($hook, $entityName, CRM_Utils_Array::value('id', $params), $params);
$instance = new $className();
$instance->copyValues($params);
$instance->save();
CRM_Utils_Hook::post($hook, $entityName, $instance->id, $instance);
return $instance;
}
/**
* Get a 'complex' invoice.
*
* Only call this via the api... I made it public static because of the move away from 'real' BAO
* classes in preparation for doctrine but the api is the way to go.
*
* @param array $params
*
* @return array
*/
public static function getDerived($params) {
try{
// ok this chaining is a bit heavy but it would be good to work towards API returning this more efficiently
// ie. not keen on the fact you can't easily get line items for contributions related to participants
// & TBH the communication with Xero takes 90% of the script time ...
// @todo - set return properties on contribution.get
// @todo at the moment we use getsingle because we are only dealing with 1 but should alter to 'get'
// as in theory any params could be passed in - resulting in many - there are some api
// issues around getfields to resolve though - see notes on api
$contribution = civicrm_api3('contribution', 'getsingle', array_merge(array(
'api.participant_payment.get' => array(
'return' => 'api.participant., participant_id',
'api.participant.get' => array(
'api.line_item.get' => 1,
'return' => 'participant_source, event_id, financial_type_id',
),
),
), $params));
// There is a chaining bug on line item because chaining passes contribution_id along as entity_id.
// CRM-16522.
$contribution['api.line_item.get'] = civicrm_api3('line_item', 'get', array(
'contribution_id' => $contribution['id'],
));
if ($contribution['api.line_item.get']['count']) {
$contribution['line_items'] = $contribution['api.line_item.get']['values'];
}
else {
//we'll keep the participant record for anyone trying to do hooks
$contribution['participant'] = $contribution['api.participant_payment.get']['values'][0]['api.participant.get']['values'][0];
$contribution['line_items'] = $contribution['participant']['api.line_item.get']['values'];
//if multiple participants one line item each
self::_getAdditionalParticipanLineItems($contribution);
}
foreach ($contribution['line_items'] as &$lineItem) {
$lineItem['accounting_code'] = CRM_Financial_BAO_FinancialAccount::getAccountingCode($lineItem['financial_type_id']);
$lineItem['accounts_contact_id'] = self::getAccountsContact($lineItem['financial_type_id']);
$contributionAccountsContactIDs[$lineItem['accounts_contact_id']] = TRUE;
if (!isset($lineItem['contact_id'])) {
//this would have been set for a secondary participant above so we are ensuring primary ones have it
// for conformity & ease downstream
$lineItem['contact_id'] = $contribution['contact_id'];
}
if (!isset($lineItem['display_name'])) {
//this would have been set for a secondary participant above so we are ensuring primary ones have it
// for conformity & ease downstream
$lineItem['display_name'] = $contribution['display_name'];
}
}
//@todo move the getAccountingCode to a fn that caches it
$contribution['accounting_code'] = CRM_Financial_BAO_FinancialAccount::getAccountingCode($contribution['financial_type_id']);
$contribution['accounts_contact_id'] = array_keys($contributionAccountsContactIDs);
}
catch(Exception $e) {
// probably shouldn't catch & let calling class catch
}
// In 4.6 this might be more reliable as Monish did some tidy up on BAO_Search stuff.
// Relying on it being unique makes me nervous...
if (empty($contribution['payment_instrument_id'])) {
$paymentInstruments = civicrm_api3('contribution', 'getoptions', array('field' => 'payment_instrument_id'));
$contribution['payment_instrument_id'] = array_search($contribution['payment_instrument'], $paymentInstruments['values']);
}
$contribution['payment_instrument_financial_account_id'] = CRM_Financial_BAO_FinancialTypeAccount::getInstrumentFinancialAccount($contribution['payment_instrument_id']);
try {
$contribution['payment_instrument_accounting_code'] = civicrm_api3('financial_account', 'getvalue', array(
'id' => $contribution['payment_instrument_financial_account_id'],
'return' => 'accounting_code',
));
}
catch(Exception $e) {
;
}
return array($contribution['id'] => $contribution);
}
/**
* Get Line items for invoice.
*
* At this stage only secondary participants are being fetched here.
*
* @param array $invoice Invoice array being prepared for Xero
*
* @return array|null
* Line items if there are some else null
*/
protected static function _getAdditionalParticipanLineItems(&$invoice) {
$rowsTotal = 0;
if (!is_array($invoice['line_items'])) {
// this seems to occur when the participant record has been deleted & not the contribution record
$invoice['line_items'] = array();
return;
}
foreach ($invoice['line_items'] as $line_item) {
$rowsTotal .= $line_item['line_total'];
}
if ($invoice['total_amount'] > $rowsTotal) {
// api let us down use direct sql
// @TODO get api to accept the below
// $otherParticipants = civicrm_api('Participant','Get',array('version' =>3,'participant_registered_by_id' =>$invoice['participant_id']));
$sql = "SELECT p.id, contact_id, display_name FROM civicrm_participant p
LEFT JOIN civicrm_contact c ON c.id = p.contact_id
WHERE registered_by_id = %1";
$dao = CRM_Core_DAO::executeQuery($sql, array(1 => array($invoice['participant']['id'], 'Integer')));
while ($dao->fetch()) {
$lineItems = civicrm_api3('line_item', 'get', array(
'sequential' => 1,
'entity_id' => $dao->id,
'entity_table' => 'civicrm_participant',
));
$invoice['line_items'][] = array_merge($lineItems['values'][0], array('contact_id' => $dao->contact_id, 'display_name' => $dao->display_name));
}
}
}
/**
* Update contributions in civicrm based on their status in Xero.
*/
public static function completeContributionFromAccountsStatus() {
$sql = "
SELECT contribution_id, receive_date
FROM civicrm_account_invoice cas
LEFT JOIN civicrm_contribution civi ON cas.contribution_id = civi.id
WHERE civi.contribution_status_id =2
AND accounts_status_id = 1
";
$dao = CRM_Core_DAO::executeQuery($sql);
// Get send receipt override
$isSendReceipt = civicrm_api3('Setting', 'getvalue', array(
'domain_id' => CRM_Core_Config::domainID(),
'name' => 'account_sync_send_receipt',
));
switch ($isSendReceipt) {
case 'send':
$send_receipt = 1;
break;
case 'do_not_send':
$send_receipt = 0;
break;
default:
$send_receipt = NULL;
break;
}
while ($dao->fetch()) {
$params = array('id' => $dao->contribution_id, 'receive_date' => $dao->receive_date);
if (is_numeric($isSendReceipt)) {
$params['is_email_receipt'] = $send_receipt;
}
civicrm_api3('contribution', 'completetransaction', $params);
}
}
/**
* Cancel contribution in Civi based on Xero Status.
*
* @todo - I don't believe this will adequately cancel related entities
*/
public static function cancelContributionFromAccountsStatus($params) {
//get pending registrations
$sql = "SELECT cas.contribution_id
FROM civicrm_account_invoice cas
LEFT JOIN civicrm_contribution civi ON cas.contribution_id = civi.id
WHERE accounts_status_id =3
";
$dao = CRM_Core_DAO::executeQuery($sql);
while ($dao->fetch()) {
$params['contribution_status_id'] = 3;
$params['id'] = $dao->contribution_id;
civicrm_api3('Contribution', 'Create', $params);
}
}
/**
* Get the financial_contact from the financial type id.
*
* @param int $financialTypeID
*
* @return mixed
*/
public static function getAccountsContact($financialTypeID) {
static $contacts = array();
if (empty($contacts[$financialTypeID])) {
$accountingCode = self::getAccountCode($financialTypeID);
$contacts[$financialTypeID] = CRM_Core_DAO::singleValueQuery(
"SELECT contact_id FROM civicrm_financial_account
WHERE accounting_code = %1
",
array(1 => array($accountingCode, 'String'))
);
}
return $contacts[$financialTypeID];
}
/**
* Get the accounting code from the financial type id.
*
* @param int $financialTypeID *
*
* @return string
* @throws \CRM_Core_Exception
*/
public static function getAccountCode($financialTypeID) {
static $codes = array();
if (!in_array($financialTypeID, $codes)) {
$codes[$financialTypeID] = CRM_Financial_BAO_FinancialAccount::getAccountingCode($financialTypeID);
}
if ($codes[$financialTypeID] === NULL) {
throw new CRM_Core_Exception("No Income Account code configured for financial type $financialTypeID");
}
return $codes[$financialTypeID];
}
}
<?php
class CRM_Accountsync_BAO_AccountLineItems extends CRM_Accountsync_DAO_AccountLineItems {
/**
* Create a new AccountLineItems based on array-data
*
* @param array $params key-value pairs
* @return CRM_Accountsync_DAO_AccountLineItems|NULL
*
public static function create($params) {
$className = 'CRM_Accountsync_DAO_AccountLineItems';
$entityName = 'AccountLineItems';
$hook = empty($params['id']) ? 'create' : 'edit';
CRM_Utils_Hook::pre($hook, $entityName, CRM_Utils_Array::value('id', $params), $params);
$instance = new $className();
$instance->copyValues($params);
$instance->save();
CRM_Utils_Hook::post($hook, $entityName, $instance->id, $instance);
return $instance;
} */
}
<?php
class CRM_Accountsync_BAO_AccountPayment extends CRM_Accountsync_DAO_AccountPayment {
/**
* Create a new AccountPayment based on array-data
*
* @param array $params key-value pairs
* @return CRM_Accountsync_DAO_AccountPayment|NULL
*
public static function create($params) {
$className = 'CRM_Accountsync_DAO_AccountPayment';
$entityName = 'AccountPayment';
$hook = empty($params['id']) ? 'create' : 'edit';
CRM_Utils_Hook::pre($hook, $entityName, CRM_Utils_Array::value('id', $params), $params);
$instance = new $className();
$instance->copyValues($params);
$instance->save();
CRM_Utils_Hook::post($hook, $entityName, $instance->id, $instance);
return $instance;
} */
}
<?php
class CRM_Accountsync_BAO_Config extends CRM_Accountsync_DAO_AccountContact {
public static function getSupportedContributionEntities() {
return [
'Contact' => 'Contact',
'Contribution' => 'Contribution',
'ContributionRecur' => 'Recurring Contribution',
];
}
public static function getSupportedContributionCreateEntities() {
return [
'Contribution' => 'Contribution',
];
}
public static function getSupportedContactUpdateEntities() {
return [
'Contact' => 'Contact',
'Contribution' => 'Contribution',
'ContributionRecur' => 'Recurring Contribution',
'Address' => 'Address',
'Email' => 'Email',
'Phone' => 'Phone',
];
}
/**
* Get payment processors.
*
* This differs from the option value in that we append description for disambiguation.
*
* @return array
*/
public static function getPaymentProcessors() {
$results = civicrm_api3('PaymentProcessor', 'get', array(
'sequential' => 0,
'is_test' => 0,
'return' => ['id', 'name', 'description', 'domain_id'],
));
$processors = array();
foreach ($results['values'] as $processorID => $details) {
$processors[$processorID] = $details['name'];
if (!empty($details['description'])) {
$processors[$processorID] .= ' : ' . $details['description'];
}
}
return $processors;
}
}
\ No newline at end of file
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.4 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2013 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2013
*
* Generated from xml/schema/CRM/Accountsync/AccountContact.xml
* DO NOT EDIT. Generated by GenCode.php
*/
class CRM_Accountsync_DAO_AccountContact extends CRM_Core_DAO
{
/**
* static instance to hold the table name
*
* @var string
* @static
*/
static $_tableName = 'civicrm_account_contact';
/**
* static instance to hold the field values
*
* @var array
* @static
*/
static $_fields = null;
/**
* static instance to hold the keys used in $_fields for each field.
*
* @var array
* @static
*/
static $_fieldKeys = null;
/**
* static instance to hold the FK relationships
*
* @var string
* @static
*/
static $_links = null;
/**
* static instance to hold the values that can
* be imported
*
* @var array
* @static
*/
static $_import = null;
/**
* static instance to hold the values that can
* be exported
*
* @var array
* @static
*/
static $_export = null;
/**
* static value to see if we should log any modifications to
* this table in the civicrm_log table
*
* @var boolean
* @static
*/
static $_log = true;
/**
* Unique AccountContact ID
*
* @var int unsigned
*/
public $id;
/**
* FK to Contact
*
* @var int unsigned
*/
public $contact_id;
/**
* External Reference
*
* @var string
*/
public $accounts_contact_id;
/**
* Name from Accounts Package
*
* @var string
*/
public $accounts_display_name;
/**
* When was the contact last synced.
*
* @var timestamp
*/
public $last_sync_date;
/**
* When was the invoice last Altered in the accounts system.
*
* @var timestamp
*/
public $accounts_modified_date;
/**
* json array of data as returned from accounts system
*
* @var text
*/
public $accounts_data;
/**
* json array of error data as returned from accounts system
*
* @var text
*/
public $error_data;
/**
* Include in next push to accounts
*
* @var boolean
*/
public $accounts_needs_update;
/**
* Do not sync this contact.
*
* @var boolean
*/
public $do_not_sync;
/**
* Name of plugin creating the account
*
* @var string
*/
public $plugin;
/**
* class constructor
*
* @access public
* @return civicrm_account_contact
*/
function __construct()
{
$this->__table = 'civicrm_account_contact';
parent::__construct();
}
/**
* return foreign keys and entity references
*
* @static
* @access public
* @return array of CRM_Core_EntityReference
*/
static function getReferenceColumns()
{
if (!self::$_links) {
self::$_links = array(
new CRM_Core_EntityReference(self::getTableName() , 'contact_id', 'civicrm_contact', 'id') ,
);
}
return self::$_links;
}
/**
* returns all the column names of this table
*
* @access public
* @return array
*/
static function &fields()
{
if (!(self::$_fields)) {
self::$_fields = array(
'id' => array(
'name' => 'id',
'type' => CRM_Utils_Type::T_INT,
'required' => TRUE,
) ,
'contact_id' => array(
'name' => 'contact_id',
'type' => CRM_Utils_Type::T_INT,
'FKClassName' => 'CRM_Contact_DAO_Contact',
) ,
'accounts_contact_id' => array(
'name' => 'accounts_contact_id',
'type' => CRM_Utils_Type::T_STRING,
'maxlength' => 128,
'size' => CRM_Utils_Type::HUGE,
) ,
'accounts_display_name' => array(
'name' => 'accounts_display_name',
'type' => CRM_Utils_Type::T_STRING,
'title' => ts('Display Name') ,
'maxlength' => 128,
'size' => CRM_Utils_Type::HUGE,
) ,
'last_sync_date' => array(
'name' => 'last_sync_date',
// we are trying to fool the DAO here as it has funny ideas about timestamps
'type' => CRM_Utils_Type::T_DATE,
'title' => ts('Last Sync Date') ,
'default' => 'CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP',
) ,
'accounts_modified_date' => array(
'name' => 'accounts_modified_date',
// we are trying to fool the DAO here as it has funny ideas about timestamps
'type' => CRM_Utils_Type::T_DATE,
'title' => ts('Accounts Modified Date') ,
) ,
'accounts_data' => array(
'name' => 'accounts_data',
'type' => CRM_Utils_Type::T_TEXT,
'title' => ts('Account System Data') ,
) ,
'error_data' => array(
'name' => 'error_data',
'type' => CRM_Utils_Type::T_TEXT,
'title' => ts('Account Error Data') ,
) ,
'accounts_needs_update' => array(
'name' => 'accounts_needs_update',
'type' => CRM_Utils_Type::T_BOOLEAN,
'title' => ts('Accounts Needs Update') ,
'default' => '1',
) ,
'connector_id' => array(
'name' => 'connector_id',
'type' => CRM_Utils_Type::T_INT,
),
'plugin' => array(
'name' => 'plugin',
'type' => CRM_Utils_Type::T_STRING,
'title' => ts('Account Plugin') ,
'maxlength' => 32,
'size' => CRM_Utils_Type::MEDIUM,
) ,
'do_not_sync' => array(
'name' => 'do_not_sync',
'type' => CRM_Utils_Type::T_BOOLEAN,
'title' => ts('Do Not Sync') ,
'default' => '0',
),
);
}
return self::$_fields;
}
/**
* Returns an array containing, for each field, the arary key used for that
* field in self::$_fields.
*
* @access public
* @return array
*/
static function &fieldKeys()
{
if (!(self::$_fieldKeys)) {
self::$_fieldKeys = array(
'id' => 'id',
'contact_id' => 'contact_id',
'accounts_contact_id' => 'accounts_contact_id',
'accounts_display_name' => 'accounts_display_name',
'last_sync_date' => 'last_sync_date',
'accounts_modified_date' => 'accounts_modified_date',
'accounts_data' => 'accounts_data',
'error_data' => 'error_data',
'accounts_needs_update' => 'accounts_needs_update',
'do_not_sync' => 'do_not_sync',
'connector_id' => 'connector_id',
'plugin' => 'plugin',
);
}
return self::$_fieldKeys;
}
/**
* returns the names of this table
*