Skip to content

Commit

Permalink
Add API4 Xero.TrackingCategoryPull and TrackingCategoryAddOption
Browse files Browse the repository at this point in the history
  • Loading branch information
mattwire committed Jan 10, 2024
1 parent 6e217b2 commit feec475
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 18 deletions.
40 changes: 34 additions & 6 deletions CRM/Civixero/Base.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,18 @@
*/
class CRM_Civixero_Base {

private static $singleton = [];
private static array $singleton = [];

private $_xero_access_token;
/**
* @var \League\OAuth2\Client\Token\AccessToken
*/
private AccessToken $_xero_access_token;

private $_xero_tenant_id;
private string $_xero_tenant_id;

protected $_plugin = 'xero';
protected string $_plugin = 'xero';

protected $accounts_contact;
protected array $accounts_contact;

/**
* Connector ID.
Expand All @@ -32,7 +35,21 @@ class CRM_Civixero_Base {
/**
* @var \CRM_Civixero_Settings
*/
protected $settings;
protected CRM_Civixero_Settings $settings;

/**
* @return \League\OAuth2\Client\Token\AccessToken
*/
protected function getAccessToken(): AccessToken {
return $this->_xero_access_token;
}

/**
* @return string
*/
protected function getTenantID(): string {
return $this->_xero_tenant_id;
}

/**
* Class constructor.
Expand All @@ -53,6 +70,17 @@ public function __construct($parameters = []) {
$this->singleton($this->_xero_access_token->getToken(), $this->_xero_tenant_id, $this->connector_id, $force);
}

public function getAccountingApiInstance(): \XeroAPI\XeroPHP\Api\AccountingApi {
// Configure OAuth2 access token for authorization: OAuth2
$config = \XeroAPI\XeroPHP\Configuration::getDefaultConfiguration()->setAccessToken($this->getAccessToken());

$apiInstance = new \XeroAPI\XeroPHP\Api\AccountingApi(
new \GuzzleHttp\Client(),
$config
);
return $apiInstance;
}

/**
* Get the contact that the connector account is associated with.
*
Expand Down
87 changes: 75 additions & 12 deletions CRM/Civixero/TrackingCategory.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,86 @@ class CRM_Civixero_TrackingCategory extends CRM_Civixero_Base {
* We call the civicrm_accountPullPreSave hook so other modules can alter if required
* - I can't think of a reason why they would but it seems consistent
*
* @param array $params
* @throws \Exception
*/
function pull($params) {
public function pull() {
CRM_Civixero_Base::isApiRateLimitExceeded(TRUE);

static $trackingOptions = [];
if (empty($trackingOptions)) {
$tc = $this->getSingleton($this->connector_id)->TrackingCategories();
foreach ($tc['TrackingCategories'] as $trackingCategory) {
$trackingOptions[$trackingCategory['Name']]['Name'] = $trackingCategory['Name'];
$trackingOptions[$trackingCategory['Name']]['Status'] = $trackingCategory['Status'];
$trackingOptions[$trackingCategory['Name']]['TrackingCategoryID'] = $trackingCategory['TrackingCategoryID'];
foreach ($trackingCategory['Options']['Option'] as $key => $value) {
$trackingOptions[$trackingCategory['Name']]['Options'][$value['TrackingOptionID']] = $value['Name'];
static $trackingCategories = [];
if (empty($trackingCategories)) {
$where = 'Status=="' . \XeroAPI\XeroPHP\Models\Accounting\TrackingCategory::STATUS_ACTIVE . '"';
$order = "Name ASC";

try {
$categories = $this->getAccountingApiInstance()->getTrackingCategories($this->getTenantID(), $where, $order, TRUE);
foreach ($categories as $category) {
$trackingCategories[$category->getTrackingCategoryId()] = [
'name' => $category->getName(),
'status' => $category->getStatus(),
'id' => $category->getTrackingCategoryId(),
];
foreach ($category->getOptions() as $option) {
$trackingCategories[$category->getTrackingCategoryId()]['options'][] = [
'name' => $option->getName(),
'status' => $option->getStatus(),
'id' => $option->getTrackingOptionId(),
];
}
}
} catch (\Exception $e) {
\Civi::log('civixero')->error('Exception when calling AccountingApi->getTrackingCategories: ' . $e->getMessage());
throw $e;
}
}
return $trackingCategories;
}

/**
* @throws \Exception
*/
public function addOption(string $trackingCategoryID, string $trackingOptionName): array {
CRM_Civixero_Base::isApiRateLimitExceeded(TRUE);

$apiInstance = $this->getAccountingApiInstance();

$trackingOption = new XeroAPI\XeroPHP\Models\Accounting\TrackingOption;
$trackingOption->setName($trackingOptionName);
$idempotencyKey = md5(rand() . microtime());

try {
$trackingCategoryOption = $this->checkIfCategoryHasOption($trackingCategoryID, $trackingOptionName);
if (!empty($trackingCategoryOption)) {
return $trackingCategoryOption;
}
$result = $apiInstance->createTrackingOptions($this->getTenantID(), $trackingCategoryID, $trackingOption, $idempotencyKey);
return [
'name' => $result->getOptions()[0]->getName(),
'status' => $result->getOptions()[0]->getStatus(),
'id' => $result->getOptions()[0]->getTrackingOptionId(),
'added' => TRUE,
];
} catch (\Exception $e) {
\Civi::log('civixero')->error('Exception when calling AccountingApi->createTrackingOptions: ' . $e->getMessage());
throw $e;
}
}

/**
* @throws \Exception
*/
public function checkIfCategoryHasOption(string $trackingCategoryID, string $trackingOptionName): array {
$trackingCategories = $this->pull();
if (!isset($trackingCategories[$trackingCategoryID])) {
throw new \Exception('Tracking Category with ID ' . $trackingCategoryID . ' does not exist');
}

foreach ($trackingCategories[$trackingCategoryID]['options'] as $trackingCategoryOption) {
if ($trackingCategoryOption['name'] === $trackingOptionName) {
$trackingCategoryOption['added'] = FALSE;
return $trackingCategoryOption;
}
}
return $trackingOptions;
return [];
}

}
45 changes: 45 additions & 0 deletions Civi/Api4/Action/Xero/TrackingCategoryAddOption.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

namespace Civi\Api4\Action\Xero;

use CRM_Civixero_ExtensionUtil as E;

/**
* This API Action creates a payment. It is based on API3 Payment.create and API3 MJWPayment.create
*
*/
class TrackingCategoryAddOption extends \Civi\Api4\Generic\AbstractCreateAction {

/**
* Connector ID (default 0)
*
* @var int
*/
protected int $connectorID = 0;

/**
* @var string The Xero Tracking Category ID
*/
protected string $trackingCategoryID;

/**
* @var string The Tracking Category option name
*/
protected string $optionName;

/**
*
* Note that the result class is that of the annotation below, not the h
* in the method (which must match the parent class)
*
* @var \Civi\Api4\Generic\Result $result
*/
public function _run(\Civi\Api4\Generic\Result $result) {
$params = ['connector_id' => $this->connectorID];
$xero = new \CRM_Civixero_TrackingCategory($params);
$trackingOption = $xero->addOption($this->trackingCategoryID, $this->optionName);
$result->exchangeArray($trackingOption);
return $result;
}

}
35 changes: 35 additions & 0 deletions Civi/Api4/Action/Xero/TrackingCategoryPull.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace Civi\Api4\Action\Xero;

use CRM_Civixero_ExtensionUtil as E;

/**
* This API Action creates a payment. It is based on API3 Payment.create and API3 MJWPayment.create
*
*/
class TrackingCategoryPull extends \Civi\Api4\Generic\AbstractAction {

/**
* Connector ID (default 0)
*
* @var int
*/
protected int $connectorID = 0;

/**
*
* Note that the result class is that of the annotation below, not the h
* in the method (which must match the parent class)
*
* @var \Civi\Api4\Generic\Result $result
*/
public function _run(\Civi\Api4\Generic\Result $result) {
$params = ['connector_id' => $this->connectorID];
$xero = new \CRM_Civixero_TrackingCategory($params);
$trackingCategories = $xero->pull();
$result->exchangeArray($trackingCategories ?? []);
return $result;
}

}
24 changes: 24 additions & 0 deletions Civi/Api4/Xero.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace Civi\Api4;

/**
* CiviCRM Xero API
*
* @searchable none
* @since 5.19
* @package Civi\Api4
*/
class Xero extends Generic\AbstractEntity {

/**
* @param bool $checkPermissions
* @return Generic\BasicGetFieldsAction
*/
public static function getFields($checkPermissions = TRUE) {
return (new Generic\BasicGetFieldsAction(__CLASS__, __FUNCTION__, function() {
return [];
}))->setCheckPermissions($checkPermissions);
}

}
51 changes: 51 additions & 0 deletions mixin/smarty-v2@1.0.1.mixin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

/**
* Auto-register "templates/" folder.
*
* @mixinName smarty-v2
* @mixinVersion 1.0.1
* @since 5.59
*
* @param CRM_Extension_MixInfo $mixInfo
* On newer deployments, this will be an instance of MixInfo. On older deployments, Civix may polyfill with a work-a-like.
* @param \CRM_Extension_BootCache $bootCache
* On newer deployments, this will be an instance of MixInfo. On older deployments, Civix may polyfill with a work-a-like.
*/
return function ($mixInfo, $bootCache) {
$dir = $mixInfo->getPath('templates');
if (!file_exists($dir)) {
return;
}

$register = function() use ($dir) {
// This implementation has a theoretical edge-case bug on older versions of CiviCRM where a template could
// be registered more than once.
CRM_Core_Smarty::singleton()->addTemplateDir($dir);
};

// Let's figure out what environment we're in -- so that we know the best way to call $register().

if (!empty($GLOBALS['_CIVIX_MIXIN_POLYFILL'])) {
// Polyfill Loader (v<=5.45): We're already in the middle of firing `hook_config`.
if ($mixInfo->isActive()) {
$register();
}
return;
}

if (CRM_Extension_System::singleton()->getManager()->extensionIsBeingInstalledOrEnabled($mixInfo->longName)) {
// New Install, Standard Loader: The extension has just been enabled, and we're now setting it up.
// System has already booted. New templates may be needed for upcoming installation steps.
$register();
return;
}

// Typical Pageview, Standard Loader: Defer the actual registration for a moment -- to ensure that Smarty is online.
\Civi::dispatcher()->addListener('hook_civicrm_config', function() use ($mixInfo, $register) {
if ($mixInfo->isActive()) {
$register();
}
});

};

0 comments on commit feec475

Please sign in to comment.