Projekt

Obecné

Profil

« Předchozí | Další » 

Revize 2015

Přidáno uživatelem Ondřej Fibich před více než 11 roky(ů)

Opravy:
- commit zapomenutych souboru z commitu FIO

Zobrazit rozdíly:

freenetis/branches/1.1/application/libraries/Bank_Statement_File_Importer.php
<?php defined('SYSPATH') or die('No direct script access.');
/*
* This file is part of open source system FreenetIS
* and it is released under GPLv3 licence.
*
* More info about licence can be found:
* http://www.gnu.org/licenses/gpl-3.0.html
*
* More info about project can be found:
* http://www.freenetis.org/
*
*/
require_once APPPATH.'libraries/importers/Duplicity_Exception.php';
/**
* Subclass for all bank statements importers of new generation (since 1.1).
* Import is made using import static method that is the only public method of
* this class (only one that is called).
*
* New importers (drivers) should implement this abstract class and then info about
* them must be added into drivers array in this class.
* Driver handles checking of format, parsing, saving data into database.
* On the other hand this class handles opening of file, notifying of affected users,
* error handling, variable key searching and grouping drivers in order to provide
* an independent of driver on another parts of system.
* Drivers should be located at directory defined by DIR constant that
* contains a relative location to this class.
*
* Lifecycle of importer is following:
*
* 1. creation
* 2. set file data (input file)
* 3. format file checking
* 4. parse
* 5. store into database
* 6. inform users
* 7. end of life
*
* @author Ondrej Fibich
* @since 1.1
*/
abstract class Bank_Statement_File_Importer
{
/**
* Dir with driver classes
*/
const DIR = 'importers';
/**
* Available drivers (bank statements importers).
* Each driver contains items:
*
* name Name
* class Class name in Varaible keys folder
* bank_type Bank type of this driver (e.g. FIO, UniCredit, ...)
* extensions Supported extension array
*
* @var array
*/
private static $drivers = array
(
/* FIO - JSON - Obtained from FIO bank API */
array
(
'name' => 'FIO JSON API importer',
'class' => 'Json_Fio_Bank_Statement_File_Importer',
'bank_type' => Bank_account_Model::TYPE_FIO,
'extensions' => array('json')
),
/* FIO - CSV - Obtained from FIO e-banking */
array
(
'name' => 'FIO CSV importer',
'class' => 'Csv_Fio_Bank_Statement_File_Importer',
'bank_type' => Bank_account_Model::TYPE_FIO,
'extensions' => array('csv')
)
);
/**
* Gets available drivers.
*
* @return array
*/
public static function get_drivers()
{
return self::$drivers;
}
/**
* Tries to download statement and then import it.
*
* This action contains just getting of a file URL from bank settings.
* Then this URL with another info is passed to import method which
* does the rest of the job.
*
* @param Bank_account_Model $bank_account Bank account ot which the statement
* is imported
* @param Bank_Account_Settings $settings
* @param boolean $send_emails Send notification of affected members by e-mail
* @param boolean $send_sms Send notification of affected members by sms
* @return Bank_statement_Model Stored statement
* @throws InvalidArgumentException On invalid bank account settings
* that cannot be use for proper download
*/
public static function download(Bank_account_Model $bank_account,
Bank_Account_Settings $settings, $send_emails, $send_sms)
{
// get type
$type = $settings->get_download_statement_type();
if (empty($type))
{
throw new InvalidArgumentException(__('Unset download statement type'));
}
// get url
$url = $settings->get_download_statement_url();
if (empty($url))
{
throw new InvalidArgumentException(__('Unset download statement URL'));
}
// obtain driver
$driver = self::factory($bank_account, $type);
$acc = $bank_account->account_nr . '/' . $bank_account->bank_nr;
if (!$driver)
{
$m = __('File importer for bank %s is not available', $acc);
throw new InvalidArgumentException($m);
}
// preparation before download
if (!$driver->before_download($bank_account, $settings))
{
throw new Exception(__('Cannot prepare for statement download'));
}
// import
return self::import($bank_account, $url, $type, $send_emails, $send_sms);
}
/**
* Imports a bank statement placed in a file that is given by the filename
* to bank account that is given by its database model. Throws error
* exceptions with translated error description if any error occures.
*
* @param Bank_account_Model $bank_account Bank account ot which the statement
* is imported
* @param string $filename Full path to imported file
* @param string $ext File extension
* @param boolean $send_emails Send notification of affected members by e-mail
* @param boolean $send_sms Send notification of affected members by sms
* @return Bank_statement_Model Stored statement
* @throws InvalidArgumentException On invalid file or bank account entity
* @throws Exception On any error during parsing or storing of statement
*/
public static function import(Bank_account_Model $bank_account,
$filename, $ext, $send_emails = TRUE, $send_sms = FALSE)
{
/* obtain driver */
$driver = self::factory($bank_account, $ext);
$acc = $bank_account->account_nr . '/' . $bank_account->bank_nr;
if (!$driver)
{
$m = __('File importer for bank %s is not available', $acc);
throw new InvalidArgumentException($m);
}
$driver->inform_affected_member_by_email = $send_emails;
$driver->inform_affected_member_by_sms = $send_sms;
/* set file data */
$fd = @file_get_contents($filename);
if ($fd == FALSE)
{
$m = __('Cannot read from input file') . ': ' . $filename;
throw new InvalidArgumentException($m);
}
$driver->set_file_data($fd);
/* check format */
if (!$driver->check_file_data_format())
{
$m = __('Invalid input file format in file "%s" caused by: %s',
array($filename, '<br>' . implode('<br>', $driver->get_errors())));
throw new Exception($m);
}
/* check header of statement */
$header_data = $driver->get_header_data();
if (!$header_data ||
$header_data->get_bank_id() != $bank_account->bank_nr ||
$header_data->get_account_id() != $bank_account->account_nr)
{
$m = __('Bank account number in listing (%s) header does not match ' .
'bank account %s in database!', array(strval($bank_account), $acc));
throw new Exception($m);
}
/* parse file */
if (!$driver->parse_file_data())
{
$m = __('Error during parsing of statement file %s caused by: %s',
array($filename, '<br>' . implode('<br>', $driver->get_errors())));
throw new Exception($m);
}
/* store result */
$bank_statement = $driver->store();
if (!$bank_statement || !$bank_statement->id)
{
$m = __('Error during storing of parsed file %s import caused by: %s',
array($filename, '<br>' . implode('<br>', $driver->get_errors())));
throw new Exception($m);
}
/* inform affected members */
$driver->notify_affected_members();
return $bank_statement;
}
/**
* Creates an instance of file importer driver that is capable of
* importing bank statement.
*
* @param Bank_account_Model $bank_account
* @param string $ext File extension
* @return Bank_Statement_File_Importer Driver or NULL if no suitable
* driver is available.
* @throws InvalidArgumentException On invalid file or bank account entity
*/
protected static function factory($bank_account, $ext)
{
// bank account check
if ($bank_account == NULL || !$bank_account->id)
{
throw new InvalidArgumentException(__('Invalid bank account'));
}
// find suitable driver
foreach (self::$drivers as $d)
{
if ($d['bank_type'] == $bank_account->type &&
in_array($ext, $d['extensions']))
{
$cn = $d['class'];
require_once dirname(__FILE__) . '/' . self::DIR . '/' . $cn . '.php';
return new $cn($bank_account);
}
}
// not founded
return NULL;
}
/**
* Bank account model for importing of statement.
*
* @var Bank_account_Model
*/
private $bank_account = NULL;
/**
* Contains parsed file that contains a bank statement
*
* @var string
*/
private $file_data = NULL;
/**
* Error stack that contains translated errors
*
* @var arrray
*/
private $errors = array();
/**
* Array of memeber IDs that are affected by the content of file data
*
* @var array
*/
private $affected_members = array();
/**
* Inform affected by e-mail notification (ON by default) that contains
* information about the received payment.
*
* @var boolean
*/
private $inform_affected_member_by_email = TRUE;
/**
* Inform affected by SMS notification (OFF by default) that contains
* information about the received payment.
*
* @var boolean
*/
private $inform_affected_member_by_sms = FALSE;
/**
* Creates new instance od bank statement import.
*
* @param Bank_account_Model $bam
*/
protected function __construct(Bank_account_Model $bam)
{
$this->bank_account = $bam;
}
/**
* Get importer name.
*
* @return string
*/
public function get_importer_name()
{
foreach (self::$drivers as $d)
{
if ($d['class'] == get_class($this))
{
return $d['name'];
}
}
return NULL;
}
/**
* User ID of caller of this importer. (user who calls it)
*
* @return integer
*/
protected function get_user_id()
{
$user_id = Session::instance()->get('user_id', NULL); // logged user
if ($user_id) // not logged => scheduler => association user
{
$association = new Member_Model(Member_Model::ASSOCIATION);
$user_id = $association->get_main_user();
}
return $user_id;
}
/**
* Gets bank account for which the importer is evaulating imports.
*
* @return Bank_account_Model
*/
protected function get_bank_account()
{
return $this->bank_account;
}
/**
* Sets bank statement file data (content of a file)
*
* @param string $fd
*/
protected function set_file_data($fd)
{
$this->file_data = $fd;
}
/**
* Gets bank statement file data (content of a file)
*
* @return string
*/
protected function get_file_data()
{
return $this->file_data;
}
/**
* Adds given error to the start of the error stack.
*
* @param string $error Error messsage
* @param boolean $translate Should be error message translated before adding?
*/
protected function add_error($error, $translate = TRUE)
{
$this->errors = array
(
$translate ? __($error) : $error
) + $this->errors;
}
/**
* Add given exception as error to the start of the error stack.
*
* @param Exception $e
* @param boolean $translate Should be exception message translated before adding?
*/
protected function add_exception_error(Exception $e, $translate = TRUE)
{
$this->errors = array
(
($translate ? __($e->getMessage()) : $e->getMessage()) .
': ' . nl2br($e->getTraceAsString())
) + $this->errors;
}
/**
* Adds member as affected by this parsed statement. Later if statement is
* succefully parsed and saved these members are inform by notification.
*
* @param integer $member_id
*/
protected function add_affected_member($member_id)
{
$member_id = intval($member_id);
if (!in_array($member_id, $this->affected_members))
{
$this->affected_members[] = $member_id;
}
}
/**
* Gets error trace
*
* @return array
*/
protected function get_errors()
{
return $this->errors;
}
/**
* Finds member ID by variable method. If given variable symbol is not
* founded in the database and a variable key generator is active and
* capable of error correction than the variable key is tried repared
* and searched again.
*
* @staticvar Variable_Symbol_Model $vk_model
* @param string $variable_symbol
* @param boolean $error_correction May be used error correction for VS?
* @return null|integer NULL of not founded, member ID otherwise
*/
protected function find_member_by_vs($variable_symbol, $error_correction = TRUE)
{
static $vk_model = NULL;
if (!$vk_model)
{
$vk_model = new Variable_Symbol_Model();
}
// locate in database
$member_id = $vk_model->get_member_id($variable_symbol);
Log_queue_Model::warn($variable_symbol, $member_id . ' - ' . $vk_model->get_last_sql_query());
// located
if ($member_id)
{
return $member_id;
}
// not located try to detect error if a variable generator is used
else if ($error_correction)
{
$vkg = Variable_Key_Generator::factory();
if ($vkg)
{
if ($vkg->errorCheckAvailable())
{
if ($vkg->errorCorrectionAvailable())
{
$corrected = $vkg->errorCorrection($variable_symbol);
if ($corrected['status'])
{
$cvs = $corrected['corrected_variable_key'];
// try to locate and if it is not located
// then do not any futher error correction
return $this->find_member_by_vs($cvs, FALSE);
}
}
}
}
}
return NULL;
}
/**
* This method may be implemented in order to do some action before
* downloading of a bank statement.
*
* Does not do anything by default.
*
* @param Bank_account_Model $bank_account
* @param Bank_Account_Settings $settings
* @return boolean Successfully run?
* @throws Exception On any error
*/
protected function before_download(Bank_account_Model $bank_account,
Bank_Account_Settings $settings)
{
return TRUE;
}
/**
* Checks whether the file content that is stored into a fileData property
* has valid format. This method checks only a format (syntax), semantic
* meaning of content is examined later.
*
* An error in the format may be add into error stack (addError) that is later
* displayed to user if this function returns FALSE.
*
* @return boolean TRUE if format is corrrent, FALSE otherwise
*/
protected abstract function check_file_data_format();
/**
* Gets header data of file data format. This method is used for checking
* if the bank statement correspondes to bank account in the database.
*
* Data must be available in any time after calling of check_file_data_format
* method.
*
* An error in the format may be add into error stack (addError) that is later
* displayed to user if this function returns FALSE.
*
* @return Header_Data
*/
protected abstract function get_header_data();
/**
* Parses a file data into a semantic interpretation of its content.
* This interpretation must be defined by implementing class.
*
* An error in the format may be add into error stack (addError) that is later
* displayed to user if this function returns FALSE.
*
* @return boolean TRUE if file data were succesfully parsed, FALSE otherwise
*/
protected abstract function parse_file_data();
/**
* Stores data that are obtain via parse_file_data method (stored internally
* - so it is in hand of the implemented file importer).
*
* All transfers are stored in the database and grouped using statement
* entity. This statement entity is than returned.
*
* This method should also set affected members in order to notify them.
*
* An error in the format may be add into error stack (addError) that is later
* displayed to user if this function returns FALSE.
*
* @param array $stats Statistics about imported statement
* @return Bank_statement_Model Bank statement that was stored or NULL
* if bank statement was not stored
* @throws Duplicity_Exception On transfers that were already imported
*/
protected abstract function store(&$stats = array());
/**
* Informs affected members by email or SMS according to setting variables
* inform_affected_member_by_email and inform_affected_member_by_sms.
*
* @return boolean TRUE if all affected members were notified, FALSE otherwise
*/
protected function notify_affected_members()
{
try
{
$send_email = Notifications_Controller::ACTIVATE;
$send_sms = Notifications_Controller::ACTIVATE;
if (!$this->inform_affected_member_by_email)
{
$send_email = Notifications_Controller::KEEP;
}
if (!$this->inform_affected_member_by_sms)
{
$send_sms = Notifications_Controller::KEEP;
}
foreach ($this->affected_members as $member_id)
{
Message_Model::activate_special_notice(
Message_Model::RECEIVED_PAYMENT_NOTICE_MESSAGE,
$member_id, $this->get_user_id(), $send_email, $send_sms
);
}
// status ok
return FALSE;
}
catch (Exception $e)
{
$m = 'Error during notifying of affected members of a bank import';
Log::add_exception($e);
Log_queue_Model::error($m, $e);
// failed
return FALSE;
}
}
}
/**
* Class for storing of header data of file import.
*/
class Header_Data
{
/**
* Bank account ID
*
* @var integer
*/
private $account_id;
/**
* Bank ID
*
* @var integer
*/
private $bank_id;
/**
* Other properties
*
* @var array
*/
private $properties = array();
/**
* Creates new Header_Data
*
* @param integer $account_id
* @param integer $bank_id
*/
function __construct($account_id, $bank_id)
{
$this->account_id = $account_id;
$this->bank_id = $bank_id;
}
/**
* Gets bank account ID
*
* @return integer
*/
public function get_account_id()
{
return $this->account_id;
}
/**
* Gets bank ID
*
* @return integer
*/
public function get_bank_id()
{
return $this->bank_id;
}
/**
* String representation of header data
*
* @return string
*/
public function __toString()
{
return $this->account_id . '/' . $this->bank_id;
}
/**
* Gets another property
*
* @param string $name
*/
public function __get($name)
{
if (isset($this->properties[$name]))
{
return $this->properties[$name];
}
throw new InvalidArgumentException('Unknown property: ' . $name);
}
/**
* Sets another property
*
* @param string $name
* @param mixed $value
*/
public function __set($name, $value)
{
$this->properties[$name] = $value;
}
}
freenetis/branches/1.1/application/libraries/importers/Csv_Fio_Bank_Statement_File_Importer.php
<?php defined('SYSPATH') or die('No direct script access.');
/*
* This file is part of open source system FreenetIS
* and it is released under GPLv3 licence.
*
* More info about licence can be found:
* http://www.gnu.org/licenses/gpl-3.0.html
*
* More info about project can be found:
* http://www.freenetis.org/
*
*/
require dirname(__FILE__) . '/Fio_Bank_Statement_File_Importer.php';
require dirname(__FILE__) . '/Fio/FioParser.php';
/**
* FIO importer for statements in CSV format that are obtained from the FIO
* e-banking portal.
*
* It uses old version of driver for parsing.
*
* @author Ondrej Fibich
* @since 1.1
*/
class Csv_Fio_Bank_Statement_File_Importer extends Fio_Bank_Statement_File_Importer
{
/**
* Data reprezentation of import.
*
* @var array
*/
private $data = NULL;
/*
* @Override
*/
protected function check_file_data_format()
{
// reset
$this->data = NULL;
// convert encoding
$this->set_file_data(iconv('cp1250', 'UTF-8', $this->get_file_data()));
// parse (we have no function for checking)
try
{
// parse
$this->data = FioParser::parseCSV($this->get_file_data());
// correct data
foreach ($this->data as &$row)
{
if ($row['mena'] != 'CZK')
{
throw new Exception(__('Unknown currency %s!', $row['mena']));
}
// convert date
if (preg_match('/^[0-9]{1,2}\.[0-9]{1,2}\.[0-9]{4}\.$/', $row['datum']))
{
$date_arr = explode('.', $row['datum']);
$timestamp = mktime(0, 0, 0, $date_arr[1], $date_arr[0], $date_arr[2]);
$row['datum'] = date('Y-m-d', $timestamp);
}
// only transfer from Fio to Fio have 'nazev_protiuctu'
// for accounts in other banks we have to derive account name
if (!$row['nazev_protiuctu'] && $row['identifikace'])
{
$row['nazev_protiuctu'] = $row['identifikace'];
}
// convert from cents
$row['castka'] /= 100;
}
// ok
return TRUE;
}
catch (Exception $e)
{
$this->data = NULL;
$this->add_exception_error($e, FALSE);
return FALSE;
}
}
/*
* @Override
*/
protected function get_header_data()
{
if (empty($this->data))
throw new InvalidArgumentException('Check CSV first');
$fio_ph = FioParser::getListingHeader();
$hd = new Header_Data($fio_ph['account_nr'], $fio_ph['bank_nr']);
$hd->currency = 'CZK';
$hd->openingBalance = $fio_ph['opening_balance'];
$hd->closingBalance = $fio_ph['closing_balance'];
$hd->dateStart = $fio_ph['from'];
$hd->dateEnd = $fio_ph['to'];
return $hd;
}
/*
* @Override
*/
protected function parse_file_data()
{
// already parsed
return !empty($this->data);
}
/*
* @Override
*/
protected function get_parsed_transactions()
{
return $this->data;
}
}
freenetis/branches/1.1/application/libraries/importers/Fio_Bank_Statement_File_Importer.php
<?php defined('SYSPATH') or die('No direct script access.');
/*
* This file is part of open source system FreenetIS
* and it is released under GPLv3 licence.
*
* More info about licence can be found:
* http://www.gnu.org/licenses/gpl-3.0.html
*
* More info about project can be found:
* http://www.freenetis.org/
*
*/
/**
* FIO abstract importer that handles storing of transfers. Subclass add method
* for handling different input file types and format.
*
* @author Ondrej Fibich, Jiri Svitak
* @since 1.1
*/
abstract class Fio_Bank_Statement_File_Importer extends Bank_Statement_File_Importer
{
/*
* Sets last succesfully transfered transaction.
* Download of new transaction will start from this transaction.
* Transactions are identified by their transaction codes that are stored
* in the bank transfer model.
*
* @Override
*/
protected function before_download(Bank_account_Model $bank_account,
Bank_Account_Settings $settings)
{
// get last transaction ID of this bank account that is stored in database
$bt_model = new Bank_transfer_Model();
$ltc = $bt_model->get_last_transaction_code_of($bank_account->id);
if (empty($ltc) || $ltc <= 0)
{
$ltc = 0; // no transaction for this account
}
// set a start transaction for downloading of next transactions
$url = $settings->get_download_base_url() . 'set-last-id/'
. $settings->api_token . '/' . $ltc . '/';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$response = curl_exec($ch);
curl_close($ch);
// done?
if ($response !== FALSE)
{
return TRUE;
}
// error in downloading
$m = __('Setting of the last downloaded transaction has failed');
throw new Exception($m . ' (' . $response . ')');
}
/**
* Gets parsed data.
*
* @return array Contains array of transactions
*/
protected abstract function get_parsed_transactions();
/*
* @Override
*/
protected function store(&$stats = array())
{
$statement = new Bank_statement_Model();
$ba = $this->get_bank_account();
$user_id = $this->get_user_id();
try
{
/* header */
$statement->transaction_start();
$header = $this->get_header_data();
// bank statement
$statement->bank_account_id = $ba->id;
$statement->user_id = $this->get_user_id();
$statement->type = $this->get_importer_name();
$statement->from = $header->dateStart;
$statement->to = $header->dateEnd;
$statement->opening_balance = $header->openingBalance;
$statement->closing_balance = $header->closingBalance;
$statement->save_throwable();
/* transactions */
// preparation of system double-entry accounts
$suppliers = ORM::factory('account')->get_account_by_attribute(Account_attribute_Model::SUPPLIERS);
$member_fees = ORM::factory('account')->get_account_by_attribute(Account_attribute_Model::MEMBER_FEES);
$operating = ORM::factory('account')->get_account_by_attribute(Account_attribute_Model::OPERATING);
$cash = ORM::factory('account')->get_account_by_attribute(Account_attribute_Model::CASH);
$account = $ba->get_related_account_by_attribute_id(Account_attribute_Model::BANK);
$bank_interests = $ba->get_related_account_by_attribute_id(Account_attribute_Model::BANK_INTERESTS);
// model preparation
$bt = new Bank_transfer_Model();
$fee_model = new Fee_Model();
// statistics preparation
$stats['unidentified_nr'] = 0;
$stats['invoices'] = 0;
$stats['invoices_nr'] = 0;
$stats['member_fees'] = 0;
$stats['member_fees_nr'] = 0;
$stats['interests'] = 0;
$stats['interests_nr'] = 0;
$stats['deposits'] = 0;
$stats['deposits_nr'] = 0;
// miscellaneous preparation
$now = date('Y-m-d H:i:s');
$number = 0;
// imported transaction codes, to check duplicities
$transaction_codes = array();
// saving each bank listing item
foreach ($this->get_parsed_transactions() as $item)
{
// convert date of transfer to international format
$datetime = $item['datum'];
// try to find counter bank account in database
$counter_ba = ORM::factory('bank_account')->where(array
(
'account_nr' => $item['protiucet'],
'bank_nr' => $item['kod_banky']
))->find();
// counter bank account does not exist? let's create new one
if (!$counter_ba->id)
{
$counter_ba->clear();
$counter_ba->set_logger(FALSE);
$counter_ba->name = $item['nazev_protiuctu'];
$counter_ba->account_nr = $item['protiucet'];
$counter_ba->bank_nr = $item['kod_banky'];
$counter_ba->member_id = NULL;
$counter_ba->save_throwable();
}
// determining in/out type of transfer
if ($item['castka'] < 0)
{
// outbound transfer
// -----------------
// by default we assume, it is "invoice" (this includes all expenses)
// double-entry transfer
$transfer_id = Transfer_Model::insert_transfer(
$account->id, $suppliers->id, null,
$counter_ba->member_id, $user_id, null,
$datetime, $now, $item['zprava'], abs($item['castka'])
);
// bank transfer
$bt->clear();
$bt->set_logger(false);
$bt->origin_id = $ba->id;
$bt->destination_id = $counter_ba->id;
$bt->transfer_id = $transfer_id;
$bt->bank_statement_id = $statement->id;
$bt->transaction_code = $item['id_pohybu'];
$bt->number = $number;
$bt->constant_symbol = $item['ks'];
$bt->variable_symbol = $item['vs'];
$bt->specific_symbol = $item['ss'];
$bt->save();
// stats
$stats['invoices'] += abs($item['castka']);
$stats['invoices_nr']++;
}
else
{
// inbound transfer
// ----------------
// interest transfer
if ($item['typ'] == 'Připsaný úrok')
{
// let's create interest transfer
$transfer_id = Transfer_Model::insert_transfer(
$bank_interests->id, $account->id, null, null,
$user_id, null, $datetime, $now, $item['typ'],
abs($item['castka'])
);
$bt->clear();
$bt->set_logger(false);
$bt->origin_id = null;
$bt->destination_id = $ba->id;
$bt->transfer_id = $transfer_id;
$bt->bank_statement_id = $statement->id;
$bt->transaction_code = $item['id_pohybu'];
$bt->number = $number;
$bt->save();
$stats['interests'] += abs($item['castka']);
$stats['interests_nr']++;
}
elseif ($item['typ'] == 'Vklad pokladnou')
{
$member_id = $this->find_member_by_vs($item['vs']);
if (!$member_id)
{
// let's create interest transfer
$transfer_id = Transfer_Model::insert_transfer(
$cash->id, $account->id, null, null,
$user_id, null, $datetime, $now, $item['typ'],
abs($item['castka'])
);
$bt->clear();
$bt->set_logger(false);
$bt->origin_id = null;
$bt->destination_id = $ba->id;
$bt->transfer_id = $transfer_id;
$bt->bank_statement_id = $statement->id;
$bt->transaction_code = $item['id_pohybu'];
$bt->number = $number;
$bt->constant_symbol = $item['ks'];
$bt->variable_symbol = $item['vs'];
$bt->specific_symbol = $item['ss'];
$bt->save();
$stats['deposits'] += abs($item['castka']);
$stats['deposits_nr']++;
}
else
{
// double-entry incoming transfer
$transfer_id = Transfer_Model::insert_transfer(
$member_fees->id, $account->id, null, $member_id,
$user_id, null, $datetime, $now, $item['zprava'],
abs($item['castka'])
);
// incoming bank transfer
$bt->clear();
$bt->set_logger(false);
$bt->origin_id = $counter_ba->id;
$bt->destination_id = $ba->id;
$bt->transfer_id = $transfer_id;
$bt->bank_statement_id = $statement->id;
$bt->transaction_code = $item['id_pohybu'];
$bt->number = $number;
$bt->constant_symbol = $item['ks'];
$bt->variable_symbol = $item['vs'];
$bt->specific_symbol = $item['ss'];
$bt->save();
// assign transfer? (0 - invalid id, 1 - assoc id, other are ordinary members)
if ($member_id && $member_id != Member_Model::ASSOCIATION)
{
$ca = ORM::factory('account')
->where('member_id', $member_id)
->find();
// has credit account?
if ($ca->id)
{
// add affected member for notification
$this->add_affected_member($member_id);
// assigning transfer
$a_transfer_id = Transfer_Model::insert_transfer(
$account->id, $ca->id, $transfer_id, $member_id,
$user_id, null, $datetime, $now,
__('Assigning of transfer'), abs($item['castka'])
);
// transaction fee
$fee = $fee_model->get_by_date_type(
$datetime, 'transfer fee'
);
if ($fee && $fee->fee > 0)
{
$tf_transfer_id = Transfer_Model::insert_transfer(
$ca->id, $operating->id, $transfer_id,
$member_id, $user_id, null, $datetime,
$now, __('Transfer fee'), $fee->fee
);
}
$counter_ba->member_id = $member_id;
$counter_ba->save_throwable();
}
}
// member fee stats
$stats['member_fees'] += abs($item['castka']);
$stats['member_fees_nr']++;
}
}
// otherwise we assume that it is member fee
else
{
// let's identify member
$member_id = $this->find_member_by_vs($item['vs']);
if (!$member_id)
{
$stats['unidentified_nr']++;
}
// double-entry incoming transfer
$transfer_id = Transfer_Model::insert_transfer(
$member_fees->id, $account->id, null, $member_id,
$user_id, null, $datetime, $now, $item['zprava'],
abs($item['castka'])
);
// incoming bank transfer
$bt->clear();
$bt->set_logger(false);
$bt->origin_id = $counter_ba->id;
$bt->destination_id = $ba->id;
$bt->transfer_id = $transfer_id;
$bt->bank_statement_id = $statement->id;
$bt->transaction_code = $item['id_pohybu'];
$bt->number = $number;
$bt->constant_symbol = $item['ks'];
$bt->variable_symbol = $item['vs'];
$bt->specific_symbol = $item['ss'];
$bt->save();
// assign transfer? (0 - invalid id, 1 - assoc id, other are ordinary members)
if ($member_id && $member_id != Member_Model::ASSOCIATION)
{
$ca = ORM::factory('account')
->where('member_id', $member_id)
->find();
// has credit account?
if ($ca->id)
{
// add affected member for notification
$this->add_affected_member($member_id);
// assigning transfer
$a_transfer_id = Transfer_Model::insert_transfer(
$account->id, $ca->id, $transfer_id, $member_id,
$user_id, null, $datetime, $now,
__('Assigning of transfer'), abs($item['castka'])
);
// transaction fee
$fee = $fee_model->get_by_date_type(
$datetime, 'transfer fee'
);
if ($fee && $fee->fee > 0)
{
$tf_transfer_id = Transfer_Model::insert_transfer(
$ca->id, $operating->id, $transfer_id,
$member_id, $user_id, null, $datetime,
$now, __('Transfer fee'), $fee->fee
);
}
$counter_ba->member_id = $member_id;
$counter_ba->save_throwable();
}
}
// member fee stats
$stats['member_fees'] += abs($item['castka']);
$stats['member_fees_nr']++;
}
}
// add item transaction code to array to check duplicities later
$transaction_codes[] = $item['id_pohybu'];
// line number increase
$number++;
}
// let's check duplicities
$duplicities = $bt->get_transaction_code_duplicities($transaction_codes, $ba->id);
if (count($duplicities) > count($transaction_codes))
{
$dm = __('Duplicate transaction codes') . ': ' . implode(', ', $duplicities);
throw new Duplicity_Exception($dm);
}
// done
$statement->transaction_commit();
// return
return $statement;
}
catch (Duplicity_Exception $e)
{
throw $e;
}
catch (Exception $e)
{
$statement->transaction_rollback();
Log::add_exception($e);
$this->add_exception_error($e);
return NULL;
}
}
}
freenetis/branches/1.1/application/libraries/importers/Json_Fio_Bank_Statement_File_Importer.php
<?php defined('SYSPATH') or die('No direct script access.');
/*
* This file is part of open source system FreenetIS
* and it is released under GPLv3 licence.
*
* More info about licence can be found:
* http://www.gnu.org/licenses/gpl-3.0.html
*
* More info about project can be found:
* http://www.freenetis.org/
*
*/
require dirname(__FILE__) . '/Fio_Bank_Statement_File_Importer.php';
/**
* FIO importer for statements in JSON format that are obtained from the FIO API.
*
* @author Ondrej Fibich
* @since 1.1
*/
class Json_Fio_Bank_Statement_File_Importer extends Fio_Bank_Statement_File_Importer
{
/**
* Reprezentation of import.
*
* @var array
*/
private $data = NULL;
/**
* Parsed reprezentation of import (transactions without header).
*
* @var array
*/
private $parsed_transactions = NULL;
/*
* @Override
*/
protected function check_file_data_format()
{
// check JSON format
$json = json_decode($this->get_file_data(), TRUE);
if (!$json)
{
return FALSE; // invalid
}
// check content fields
if (!is_array($json) ||
!array_key_exists('accountStatement', $json) ||
!array_key_exists('info', $json['accountStatement']) ||
!array_key_exists('accountId', $json['accountStatement']['info']) ||
!array_key_exists('bankId', $json['accountStatement']['info']) ||
!array_key_exists('transactionList', $json['accountStatement']))
{
return FALSE;
}
// stored parsed data
$this->data = $json;
// ok
return TRUE;
}
/*
* @Override
*/
protected function get_header_data()
{
$hd = new Header_Data(
$this->data['accountStatement']['info']['accountId'],
$this->data['accountStatement']['info']['bankId']
);
$hd->currency = $this->data['accountStatement']['info']['currency'];
$hd->iban = $this->data['accountStatement']['info']['iban'];
$hd->bic = $this->data['accountStatement']['info']['bic'];
$hd->openingBalance = $this->data['accountStatement']['info']['openingBalance'];
$hd->closingBalance = $this->data['accountStatement']['info']['closingBalance'];
$hd->dateStart = $this->data['accountStatement']['info']['dateStart'];
$hd->dateEnd = $this->data['accountStatement']['info']['dateEnd'];
$hd->idFrom = $this->data['accountStatement']['info']['idFrom'];
$hd->idTo = $this->data['accountStatement']['info']['idTo'];
$hd->idLastDownload = $this->data['accountStatement']['info']['idLastDownload'];
return $hd;
}
/*
* @Override
*/
protected function parse_file_data()
{
$this->parsed_transactions = array();
if (empty($this->data['accountStatement']['transactionList']) ||
!array_key_exists('transaction', $this->data['accountStatement']['transactionList']))
{ // no transactions available
return TRUE;
}
foreach ($this->data['accountStatement']['transactionList']['transaction'] as $t)
{
// array keys corresponds to old Fio CSV parser
$this->parsed_transactions[] = array
(
'datum' => date('Y-m-d', strtotime($t['column0']['value'])),
'id_pohybu' => $t['column22']['value'],
'id_pokynu' => $t['column17']['value'],
'kod_banky' => $t['column3']['value'],
'ks' => $t['column4']['value'],
'mena' => $t['column14']['value'],
'nazev_banky' => $t['column12']['value'],
'nazev_protiuctu' => empty($t['column10']['value']) ?
$t['column7']['value'] :
$t['column10']['value'],
'castka' => $t['column1']['value'],
'protiucet' => $t['column2']['value'],
'provedl' => $t['column9']['value'],
'prevod' => NULL, // not available
'ss' => $t['column6']['value'],
'typ' => $t['column8']['value'],
'upresneni' => $t['column22']['value'],
'identifikace' => $t['column7']['value'],
'vs' => $t['column5']['value'],
'zprava' => $t['column16']['value'],
);
}
return TRUE;
}
/*
* @Override
*/
protected function get_parsed_transactions()
{
return $this->parsed_transactions;
}
}

Také k dispozici: Unified diff