Skip to content

Commit

Permalink
Extends base for investments transactions and entities
Browse files Browse the repository at this point in the history
Tested in php7.4 with phpunit7.5
Supports adding a callback for custom DateTime library.
Created helper utils for reused code segments.
  • Loading branch information
gitmathias committed Feb 3, 2020
1 parent 4ac9407 commit 05b8ba7
Show file tree
Hide file tree
Showing 34 changed files with 1,755 additions and 182 deletions.
44 changes: 43 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ OFX Parser

[![Build Status](https://travis-ci.org/asgrim/ofxparser.svg?branch=master)](https://travis-ci.org/asgrim/ofxparser) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/asgrim/ofxparser/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/asgrim/ofxparser/?branch=master) [![Code Coverage](https://scrutinizer-ci.com/g/asgrim/ofxparser/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/asgrim/ofxparser/?branch=master) [![Latest Stable Version](https://poser.pugx.org/asgrim/ofxparser/v/stable)](https://packagist.org/packages/asgrim/ofxparser) [![License](https://poser.pugx.org/asgrim/ofxparser/license)](https://packagist.org/packages/asgrim/ofxparser)

OFX Parser is a PHP library designed to parse an OFX file downloaded from a financial institution into simple PHP objects.
OFX Parser is a PHP library designed to parse an OFX file downloaded from a financial institution into simple PHP objects.

It supports multiple Bank Accounts, the required "Sign On" response, and recognises OFX timestamps.

Expand Down Expand Up @@ -35,6 +35,48 @@ $transactions = $bankAccount->statement->transactions;

Most common nodes are support. If you come across an inaccessible node in your OFX file, please submit a pull request!

## Investments Support

Investments look much different than bank / credit card transactions. This version supports a subset of the nodes in the OFX 2.0.3 spec, per the immediate needs of the author(s). You may want to reference the OFX documentation if you choose to implement this library.

This is not a pure pass-through of fields, such as this implementation in python: [csingley/ofxtools](https://github.com/csingley/ofxtools). This package contains fields that have been "translated" on occasion to make it more friendly to those less-familiar with the investments OFX spec.

To load investments from a Quicken (QFX) file or a MS Money (OFX / XML) file:

```php
// You'll probably want to alias the namespace:
use OfxParser\Entities\Investment as InvEntities;

// Load the OFX file
$ofxParser = new \OfxParser\Parser();
$ofx = $ofxParser->loadFromFile('/path/to/your/investments_file.ofx');

// Loop over investment accounts (named bankAccounts from base lib)
foreach ($ofx->bankAccounts as $accountData) {
// Loop over transactions
foreach ($accountData->statement->transactions as $ofxEntity) {
// Keep in mind... not all properties are inherited for all transaction types...

// Maybe you'll want to do something based on the transaction properties:
$nodeName = $ofxEntity->nodeName;
if ($nodeName == 'BUYSTOCK') {
// @see OfxParser\Entities\Investment\Transaction...

$amount = abs($ofxEntity->total);
$cusip = $ofxEntity->securityId;

// ...
}

// Maybe you'll want to do something based on the entity:
if ($ofxEntity instanceof InvEntities\Transaction\BuyStock) {
// ...
}

}
}
```

## Fork & Credits

This is a fork of [grimfor/ofxparser](https://github.com/Grimfor/ofxparser) made to be framework independent. The source repo was designed for Symfony 2 framework, so credit should be given where credit due!
Expand Down
15 changes: 15 additions & 0 deletions lib/OfxParser/Entities/Inspectable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace OfxParser\Entities;

/**
* Provides entity inspection for a variety of subclasses.
*/
interface Inspectable
{
/**
* Get a list of properties defined for this entity.
* @return array array('prop_name' => 'prop_name', ...)
*/
public function getProperties();
}
36 changes: 36 additions & 0 deletions lib/OfxParser/Entities/Investment.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace OfxParser\Entities;

use SimpleXMLElement;

abstract class Investment extends AbstractEntity implements Inspectable, OfxLoadable
{
/**
* Get a list of properties defined for this entity.
*
* Since Traits are being used for multiple inheritance,
* it can be challenging to know which properties exist
* in the entity.
*
* @return array array('prop_name' => 'prop_name', ...)
*/
public function getProperties()
{
$props = array_keys(get_object_vars($this));

return array_combine($props, $props);
}

/**
* All Investment entities require a loadOfx method.
* @param SimpleXMLElement $node
* @return $this For chaining
* @throws \Exception
*/
public function loadOfx(SimpleXMLElement $node)
{
throw new \Exception('loadOfx method not defined in class "' . get_class() . '"');
}
}

29 changes: 29 additions & 0 deletions lib/OfxParser/Entities/Investment/Account.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace OfxParser\Entities\Investment;

use OfxParser\Entities\Investment;

class Account extends Investment
{
/**
* @var string
*/
public $accountNumber;

/**
* @var string
*/
public $brokerId;

/**
* @var Statement
*/
public $statement;

/**
* @var string
*/
public $transactionUid;

}
59 changes: 59 additions & 0 deletions lib/OfxParser/Entities/Investment/Transaction/Banking.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace OfxParser\Entities\Investment\Transaction;

use SimpleXMLElement;
use OfxParser\Utils;
use OfxParser\Entities\Inspectable;
use OfxParser\Entities\OfxLoadable;
use OfxParser\Entities\Transaction as BaseTransaction;

/**
* Per OFX 203 doc, this is a wrapper for a <STMTTRN> node
* (aka "Banking aggregate") with an additional <SUBACCTFUND> node.
*
* Requires Inspectable interface to match API of Invesetment entities
* extending OfxParser\Entities\Investment.
*/
class Banking extends BaseTransaction implements OfxLoadable, Inspectable
{
/**
* @var string
*/
public $nodeName = 'INVBANKTRAN';

/**
* @var string
*/
public $subAccountFund;

/**
* Get a list of properties defined for this entity.
* @return array array('prop_name' => 'prop_name', ...)
*/
public function getProperties()
{
$props = array_keys(get_object_vars($this));

return array_combine($props, $props);
}

/**
* Imports the OFX data for this node.
* @param SimpleXMLElement $node
* @return $this
*/
public function loadOfx(SimpleXMLElement $node)
{
// Duplication of code in Ofx::buildTransactions()
$this->type = (string) $node->STMTTRN->TRNTYPE;
$this->date = Utils::createDateTimeFromStr($node->STMTTRN->DTPOSTED);
$this->amount = Utils::createAmountFromStr($node->STMTTRN->TRNAMT);
$this->uniqueId = (string) $node->STMTTRN->FITID;

// Could put this in another trait.
$this->subAccountFund = (string) $node->SUBACCTFUND;

return $this;
}
}
26 changes: 26 additions & 0 deletions lib/OfxParser/Entities/Investment/Transaction/BuyMutualFund.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace OfxParser\Entities\Investment\Transaction;

use OfxParser\Entities\Investment\Transaction\Traits\BuyType;

/**
* OFX 203 doc:
* 13.9.2.4.3 Investment Buy/Sell Aggregates <INVBUY>/<INVSELL>
*
* Same as BUYSTOCK, plus <RELFITID> property.
*/
class BuyMutualFund extends BuyStock
{
/**
* @var string
*/
public $nodeName = 'BUYMF';

/**
* RELFITID used to relate transactions associated with mutual fund exchanges.
* @var string
*/
public $relatedUniqueId;
}

63 changes: 63 additions & 0 deletions lib/OfxParser/Entities/Investment/Transaction/BuySecurity.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

namespace OfxParser\Entities\Investment\Transaction;

use SimpleXMLElement;
use OfxParser\Entities\AbstractEntity;
use OfxParser\Entities\Investment;
use OfxParser\Entities\Investment\Transaction\Traits\InvTran;
use OfxParser\Entities\Investment\Transaction\Traits\SecId;
use OfxParser\Entities\Investment\Transaction\Traits\Pricing;

/**
* OFX 203 doc:
* 13.9.2.4.3 Investment Buy/Sell Aggregates <INVBUY>/<INVSELL>
*
* Properties found in the <INVBUY> aggregate.
* Used for "other securities" BUY activities and provides the
* base properties to extend for more specific activities.
*
* Required:
* <INVTRAN> aggregate
* <SECID> aggregate
* <UNITS>
* <UNITPRICE>
* <TOTAL>
* <SUBACCTSEC>
* <SUBACCTFUND>
*
* Optional:
* ...many...
*
* Partial implementation.
*/
class BuySecurity extends Investment
{
/**
* Traits used to define properties
*/
use InvTran;
use SecId;
use Pricing;

/**
* @var string
*/
public $nodeName = 'BUYOTHER';

/**
* Imports the OFX data for this node.
* @param SimpleXMLElement $node
* @return $this
*/
public function loadOfx(SimpleXMLElement $node)
{
// Transaction data is nested within <INVBUY> child node
$this->loadInvTran($node->INVBUY->INVTRAN)
->loadSecId($node->INVBUY->SECID)
->loadPricing($node->INVBUY);

return $this;
}
}

40 changes: 40 additions & 0 deletions lib/OfxParser/Entities/Investment/Transaction/BuyStock.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace OfxParser\Entities\Investment\Transaction;

use SimpleXMLElement;
use OfxParser\Entities\Investment\Transaction\Traits\BuyType;

/**
* OFX 203 doc:
* 13.9.2.4.3 Investment Buy/Sell Aggregates <INVBUY>/<INVSELL>
*
* Properties found in the <INVBUY> aggregate,
* plus <BUYTYPE> property.
*/
class BuyStock extends BuySecurity
{
/**
* Traits used to define properties
*/
use BuyType;

/**
* @var string
*/
public $nodeName = 'BUYSTOCK';

/**
* Imports the OFX data for this node.
* @param SimpleXMLElement $node
* @return $this
*/
public function loadOfx(SimpleXMLElement $node)
{
parent::loadOfx($node);
$this->loadBuyType($node);

return $this;
}
}

60 changes: 60 additions & 0 deletions lib/OfxParser/Entities/Investment/Transaction/Income.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

namespace OfxParser\Entities\Investment\Transaction;

use SimpleXMLElement;
use OfxParser\Entities\AbstractEntity;
use OfxParser\Entities\Investment;
use OfxParser\Entities\Investment\Transaction\Traits\IncomeType;
use OfxParser\Entities\Investment\Transaction\Traits\InvTran;
use OfxParser\Entities\Investment\Transaction\Traits\Pricing;
use OfxParser\Entities\Investment\Transaction\Traits\SecId;

/**
* OFX 203 doc:
* 13.9.2.4.3 Investment Buy/Sell Aggregates <INVBUY>/<INVSELL>
*
* Required:
* <INVTRAN> aggregate
* <SECID> aggregate
* <INCOMETYPE>
* <TOTAL>
* <SUBACCTSEC>
* <SUBACCTFUND>
*
* Optional:
* ...many...
*
* Partial implementation.
*/
class Income extends Investment
{
/**
* Traits used to define properties
*/
use IncomeType;
use InvTran;
use Pricing; // Not all of these are required for this node
use SecId;

/**
* @var string
*/
public $nodeName = 'INCOME';

/**
* Imports the OFX data for this node.
* @param SimpleXMLElement $node
* @return $this
*/
public function loadOfx(SimpleXMLElement $node)
{
// Transaction data is in the root
$this->loadInvTran($node->INVTRAN)
->loadSecId($node->SECID)
->loadPricing($node)
->loadIncomeType($node);

return $this;
}
}
Loading

0 comments on commit 05b8ba7

Please sign in to comment.