Projekt

Obecné

Profil

« Předchozí | Další » 

Revize 139add4b

Přidáno uživatelem David Raška před asi 9 roky(ů)

Tatra bank email importer
Support for dropdowns in bank account settings
Fixes bug in FIO importer when Duplicity_Exception occures
Fixes some bugs

Zobrazit rozdíly:

application/controllers/bank_accounts.php
case Bank_Account_Settings::FIELD_TYPE_BOOL:
$input = $form->checkbox($column)->checked($ba_driver->$column);
break;
case Bank_Account_Settings::FIELD_TYPE_DROPDOWN:
$input = $form->dropdown($column)->selected($ba_driver->$column)->options(array());
break;
default:
$input = $form->input($column)->value($ba_driver->$column);
break;
......
if (isset($info['rules']) && !empty($info['rules']))
$input->rules($info['rules']);
if (isset($info['options']) && !empty($info['options']))
$input->options($info['options']);
}
}
application/i18n/cs_CZ/help.php
'grid_hide_on_first_load' => 'Určuje, jestli skrývat grid a jeho položky při jeho první načtení. Účelem je optimalizace spotřeby paměti.',
'gps_coordinates' => 'GPS souřadnice může být ve tvaru desetinného čísla, nebo: hodiny°minuty\'sekundy&quot.',
'ignore_whitelist' => 'Umožňuje této zprávě, je-li aktivována, ignorovat bílou listinu. Určeno pro velmi speciální případy, například pokud je připojen městský úřad, který je na trvalé bílé listině, aby nebyl obtěžován a i přesto chceme jeho počítače přesměrovat nebo upozornit kvůli informaci o výpadku.',
'imap_mailbox' => 'Složka na IMAP serveru, ze které se budou stahovat e-maily. Výchozí je INBOX.',
'imap_port' => 'Výchozí port pro Žádné a STARTTLS zabezpečení je 143. Pro SSL/TLS je 993.',
'initial_immunity' => 'Počet dní, po které nemá být nový člen upozorňován na placení. Výchozí hodnota je 14 dní.',
'initial_debtor_immunity' => 'Počet dní, po které nemá být nový člen blokován a upozorňován jako dlužník. Výchozí hodnota je 35 dní.',
'log_queues_info' => 'Byly zaznamenány chyby (systému nebo některých síťových prvků), %s prosím a proveďte nezbytné akce pro jejich nápravu.',
application/i18n/cs_CZ/texts.php
'during sending an error appear' => 'Při odesílání zprávy se vyskytla chyba',
'due date' => 'Datum splatnosti',
'duplex' => 'Duplexní',
'duplicate transactions' => 'Duplikující se transakce',
'duplicate transaction codes' => 'Duplikující se transakční kódy',
'e' => 'U',
'economy' => 'Ekonomika',
application/i18n/en_US/help.php
'gps_coordinates' => 'GPS can be in shape of real number, or: hours°minutes\'seconds&quot.',
'grid_hide_on_first_load' => 'It determines whether hide grid and its items on its first load. It is used as optimalization of memory usage.',
'ignore_whitelist' => 'Allows this report, when activated, to ignore the white list. Designed for very special cases, such as when connected to the municipality, which is on permanent white list not to be harassed and even though we want to redirect his computer for information about the outage.',
'imap_mailbox' => 'Folder in IMAP server, from which e-mails will be downloaded. Default is INBOX.',
'imap_port' => 'Default port for None and STARTTLS encryption is 143. For SSL/TLS 993.',
'log_queues_info' => 'Errors has been reported (in system or in some network elements), please %s and perform the necessary actions to correct them.',
'login_name' => 'Login name is necessary for login to system. Login name can contains letters from english alphabet, numbers and dot. It has to contains at least 5 letters.',
'mail_to_field' => 'The box takes as input the user login you want to send a message. You can specify multiple recipients, separate them with commas.',
application/libraries/Bank_Account_Settings.php
const FIELD_TYPE_BOOL = 'boolean';
/** String type */
const FIELD_TYPE_STRING = 'string';
/** Dropdown type */
const FIELD_TYPE_DROPDOWN = 'dropdown';
/** Data of settings */
private $data = array();
......
case Bank_account_Model::TYPE_RAIFFEISENBANK:
require_once $cp_dir . 'Raiffeisenbank_Bank_Account_Settings.php';
return new Raiffeisenbank_Bank_Account_Settings();
case Bank_account_Model::TYPE_TATRABANKA:
require_once $cp_dir . 'Tatrabanka_Bank_Account_Settings.php';
return new Tatrabanka_Bank_Account_Settings();
}
// invalid type
......
case self::FIELD_TYPE_BOOL:
break;
case self::FIELD_TYPE_DROPDOWN:
break;
}
$this->data[$column] = $data;
application/libraries/Bank_Statement_File_Importer.php
'class' => 'Csv_Fio_Bank_Statement_File_Importer',
'bank_type' => Bank_account_Model::TYPE_FIO,
'extensions' => array('csv')
),
/* Tatrabanka - TXT - Obtained from TB B-mail */
array
(
'name' => 'Tatra bank E-mail importer',
'class' => 'Txt_Tatra_Banka_Statement_File_Importer',
'bank_type' => Bank_account_Model::TYPE_TATRABANKA,
'extensions' => array('txt')
)
);
......
{
// get type
$type = $settings->get_download_statement_type();
if (empty($type))
{
throw new InvalidArgumentException(__('Unset download statement type'));
......
// 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);
......
}
// make download
$file_data = $this->do_download($bank_account, $settings, $url);
$file_data = $driver->do_download($bank_account, $settings, $url);
if ($file_data === FALSE)
{
......
}
// import
return self::import_loaded($bank_account, $file_data, $type,
return self::import_loaded($bank_account, $file_data, $url, $type,
$send_emails, $send_sms);
}
......
{
/* obtain driver */
$driver = self::factory($bank_account, $ext);
$acc = $bank_account->account_nr . '/' . $bank_account->bank_nr;
if (!$driver)
......
protected function do_download(Bank_account_Model $bank_account,
Bank_Account_Settings $settings, $url)
{
$fd = @file_get_contents($filename);
$fd = @file_get_contents($url);
if ($fd == FALSE)
{
application/libraries/bank_account_settings/Tatrabanka_Bank_Account_Settings.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/
*
*/
/**
* Bank setting for Tatra banka accounts.
*/
class Tatrabanka_Bank_Account_Settings extends Bank_Account_Settings
{
const ENC_NONE = 1;
const ENC_STARTTLS = 2;
const ENC_SSLTLS = 3;
const DEFAULT_MAILBOX = 'INBOX';
private $encrypt = array
(
self::ENC_NONE => '/notls',
self::ENC_STARTTLS => '/tls',
self::ENC_SSLTLS => '/ssl',
);
private $port = array
(
self::ENC_NONE => 143,
self::ENC_STARTTLS => 143,
self::ENC_SSLTLS => 993,
);
/*
* @Override
*/
public function can_import_statements()
{
return FALSE;
}
/*
* @Override
*/
public function can_download_statements_automatically()
{
return isset($this->enable_download_statements_automatically) &&
$this->enable_download_statements_automatically;
}
/*
* @Override
*/
public function get_download_statement_type()
{
return 'txt';
}
/*
* @Override
*/
public function get_download_statement_url()
{
if (!isset($this->imap_server) ||empty($this->imap_server) ||
!isset($this->imap_port) ||empty($this->imap_port) ||
!isset($this->imap_mailbox) ||empty($this->imap_mailbox) ||
!array_key_exists($this->imap_encryption, $this->encrypt) ||
!array_key_exists($this->imap_encryption, $this->port))
{
throw new InvalidArgumentException(__('Invalid IMAP server settings'));
}
$encrypt = $this->encrypt[$this->imap_encryption];
$port = $this->port[$this->imap_encryption];
$hostname =
'{'.
$this->imap_server.
':'.
(empty($this->imap_port) ? $port : $this->imap_port).
'/imap'.
$encrypt.
'}'.
(empty($this->imap_mailbox) ? self::DEFAULT_MAILBOX : $this->imap_mailbox);
return $hostname;
}
/*
* @Override
*/
public function get_column_fields()
{
return array
(
// Enable auto
'enable_download_statements_automatically' => array
(
'name' => __('Enable download of statements automatically'),
'type' => self::FIELD_TYPE_BOOL,
),
// IMAP settings
'imap_server' => array
(
'name' => __('IMAP server'),
'type' => self::FIELD_TYPE_STRING,
),
'imap_port' => array
(
'name' => __('IMAP server port'),
'type' => self::FIELD_TYPE_INT,
'help' => 'imap_port'
),
'imap_encryption' => array
(
'name' => __('Encryption'),
'type' => self::FIELD_TYPE_DROPDOWN,
'options' => array
(
self::ENC_NONE => 'None',
self::ENC_STARTTLS => 'STARTTLS',
self::ENC_SSLTLS => 'SSL/TLS'
)
),
'imap_mailbox' => array
(
'name' => __('Mailbox name'),
'type' => self::FIELD_TYPE_STRING,
'help' => 'imap_mailbox'
),
// IMAP credentials
'imap_name' => array
(
'name' => __('IMAP username'),
'type' => self::FIELD_TYPE_STRING,
),
'imap_password' => array
(
'name' => __('IMAP password'),
'type' => self::FIELD_TYPE_STRING,
)
);
}
}
application/libraries/importers/Fio_Bank_Statement_File_Importer.php
}
catch (Duplicity_Exception $e)
{
$statement->transaction_rollback();
throw $e;
}
catch (Exception $e)
application/libraries/importers/Tatra_Banka_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/
*
*/
/**
* Tatra banka abstract importer that handles storing of transfers. Subclass add method
* for handling different input file types and format.
*
* @author David Raška
*/
abstract class Tatra_Banka_Statement_File_Importer extends Bank_Statement_File_Importer
{
protected $data;
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->from;
$statement->to = $header->to;
$statement->closing_balance = $header->closingBalance;
$statement->save_throwable();
/* transactions */
// preparation of system double-entry accounts
$members_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);
$account = $ba->get_related_account_by_attribute_id(Account_attribute_Model::BANK);
// 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;
// saving each bank listing item
foreach ($this->data as $item)
{
// try to find counter bank account in database
$counter_ba = ORM::factory('bank_account')->where(array
(
'account_nr' => $item['counter_account'],
'bank_nr' => $item['counter_bank']
))->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['counter_account'].'/'.$item['counter_bank'];
$counter_ba->account_nr = $item['counter_account'];
$counter_ba->bank_nr = $item['counter_bank'];
$counter_ba->member_id = NULL;
$counter_ba->save_throwable();
}
// 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(
$members_fees->id, $account->id, null, $member_id,
$user_id, null, $item['datetime'], $now, "",
abs($item['amount'])
);
// 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 = NULL;
$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);
// assign transfer
$a_transfer_id = Transfer_Model::insert_transfer(
$account->id, $ca->id, $transfer_id, $member_id,
$user_id, null, $item['datetime'], $now,
__('Assigning of transfer'), abs($item['amount'])
);
// transaction fee
$fee = $fee_model->get_by_date_type($item['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, $item['datetime'],
$now, __('Transfer fee'), $fee->fee
);
}
if (!$counter_ba->member_id)
{
$counter_ba->member_id = $member_id;
$counter_ba->save_throwable();
}
}
}
// member fee stats
$stats['member_fees'] += abs($item['amount']);
$stats['member_fees_nr']++;
$number++;
}
// let's check duplicities
$duplicities = $bt->get_transactions_duplicities($ba->id);
if ($duplicities)
{
$dm = __('Duplicate transactions') . ': ' . implode('; ', $duplicities);
throw new Duplicity_Exception($dm);
}
// done
$statement->transaction_commit();
// return
return $statement;
}
catch (Duplicity_Exception $e)
{
$statement->transaction_rollback();
throw $e;
}
catch (Exception $e)
{
$statement->transaction_rollback();
Log::add_exception($e);
$this->add_exception_error($e);
return NULL;
}
}
}
application/libraries/importers/Txt_Tatra_Banka_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__) . '/Tatra_Banka_Statement_File_Importer.php';
/**
* Parser for Tatra banka E-mail statements
*
* @author David Raška
*/
class Txt_Tatra_Banka_Statement_File_Importer extends Tatra_Banka_Statement_File_Importer
{
protected function check_file_data_format()
{
$emails = implode('', $this->get_file_data());
// Date, account, amount
$match_data = preg_match_all("@(\d{1,2}\.\d{1,2}\.\d{4} \d{1,2}:\d{2}).+(\w{2}\d{2}(\d{4})(\d{16})).* (\d+,\d{2}) (.+)\.@",
$emails,
$m1);
// Counter account
$match_counter = preg_match_all("@ (\d{4})/([\d-]+)@",
$emails,
$m2);
// Variable, specific, constant symbol
$match_symbols = preg_match_all("@/VS(\d*)/SS(\d*)/KS(\d*)@",
$emails,
$m3);
$accounts = array();
foreach ($m1[2] as $e)
{
$accounts[] = $e;
}
return ($match_data == $match_symbols) && ($match_symbols == $match_counter) && count(array_unique($accounts)) == 1;
}
protected function get_header_data()
{
$emails = $this->get_file_data();
// Newest e-mail
preg_match("@(\d{1,2}\.\d{1,2}\.\d{4} \d{1,2}:\d{2}).+(\w{2}\d{2}(\d{4})(\d{16})).* (\d+,\d{2}) (.+)\.@",
$emails[0],
$mN);
// Current balance
preg_match("@aktualny zostatok: (\d+,\d{2}) (.+)@",
$emails[0],
$mC);
// Oldest e-mail
preg_match("@(\d{1,2}\.\d{1,2}\.\d{4} \d{1,2}:\d{2}).+(\w{2}\d{2}(\d{4})(\d{16})).* (\d+,\d{2}) (.+)\.@",
$emails[count($emails) - 1],
$mO);
$hd = new Header_Data(
$mN[4], //account ID
$mN[3] //bank ID
);
$hd->from = DateTime::createFromFormat('j.n.Y G:i', $mO[1])->format('Y-m-d H:i:s');
$hd->to = DateTime::createFromFormat('j.n.Y G:i', $mN[1])->format('Y-m-d H:i:s');
$hd->closingBalance = floatval(str_replace(',', '.', $mC[1]));
return $hd;
}
protected function parse_file_data()
{
$emails = $this->get_file_data();
foreach ($emails as $email)
{
$match_data = preg_match("@(\d{1,2}\.\d{1,2}\.\d{4} \d{1,2}:\d{2}).+(\w{2}\d{2}(\d{4})(\d{16})).* (\d+,\d{2}) (.+)\.@",
$email,
$m1);
$match_counter = preg_match("@ (\d{4})/([\d-]+)@",
$email,
$m2);
$match_symbols = preg_match("@/VS(\d*)/SS(\d*)/KS(\d*)@",
$email,
$m3);
if (!$match_data || !$match_symbols || !$match_counter)
{
continue;
}
$this->data[] = array(
'datetime' => DateTime::createFromFormat('j.n.Y G:i', $m1[1])->format('Y-m-d H:i:s'),
'iban' => $m1[2],
'bank' => $m1[3],
'account' => $m1[4],
'amount' => floatval(str_replace(',', '.', $m1[5])),
'counter_account'=> $m2[2],
'counter_bank' => $m2[1],
'currency' => $m1[6],
'vs' => $m3[1],
'ss' => $m3[2],
'ks' => $m3[3]
);
}
return TRUE;
}
protected function do_download(Bank_account_Model $bank_account,
Bank_Account_Settings $settings, $url)
{
$hostname = $settings->get_download_statement_url();
$inbox = @imap_open($hostname, $settings->imap_name, $settings->imap_password, OP_READONLY);
$all_mails = array();
if ($inbox === FALSE)
{
$m = __('Cannot connect to IMAP server');
throw new Exception($m . ' (' . implode(" > ", imap_errors()) . ')');
}
$emails = imap_search($inbox, 'ALL');
if ($emails)
{
rsort($emails);
foreach ($emails as $email_number)
{
$struct = imap_fetchstructure($inbox, $email_number);
$body = imap_fetchbody($inbox,$email_number, 1);
$body = $this->decode_body($body, $struct);
if ($body === FALSE)
{
continue;
}
preg_match("@(\d{1,2}\.\d{1,2}\.\d{4} \d{1,2}:\d{2}).+(\w{2}\d{2}(\d{4})(\d{16})).* (\d+,\d{2}) (.+)\.@",
$body,
$m1);
preg_match("@ (\d{4})/([\d-]+)@",
$body,
$m2);
preg_match("@/VS(\d*)/SS(\d*)/KS(\d*)@",
$body,
$m3);
if (!$m1 || !$m2 || !$m3)
{
continue;
}
$all_mails[] = $body;
}
}
imap_close($inbox);
return $all_mails;
}
protected function decode_body($body, $struct)
{
if ($struct->type == TYPETEXT) // text body
{
// Decode text
switch ($struct->encoding)
{
case ENC7BIT:
break;
case ENC8BIT:
break;
case ENCBINARY:
throw new Exception("Unsupported IMAP encoding: ENCBINARY");
break;
case ENCBASE64:
$body = base64_decode($body);
break;
case ENCQUOTEDPRINTABLE:
$body = quoted_printable_decode($body);
break;
case ENCOTHER:
throw new Exception("Unsupported IMAP encoding: ENCOTHER");
break;
}
// Convert text to UTF-8
if (isset($struct->parameters))
{
foreach ($struct->parameters as $p)
{
if ($p->attribute == "charset")
{
$body = iconv($p->value, "UTF-8", $body);
break;
}
}
}
}
else if ($struct->type == TYPEMULTIPART) // multipart body
{
foreach ($struct->parts as $p)
{
if ($p->type == TYPETEXT)
{
$body = $this->decode_body($body, $p);
break;
}
}
}
else
{
return FALSE;
}
return $body;
}
}
application/models/bank_account.php
const TYPE_UNICREDIT = 2;
/** Raiffeisenbank */
const TYPE_RAIFFEISENBANK = 3;
/** Tatra banka */
const TYPE_TATRABANKA = 4;
/**
* Type message names
......
self::TYPE_OTHER => '',
self::TYPE_FIO => 'FIO',
self::TYPE_UNICREDIT => 'UniCredit',
self::TYPE_RAIFFEISENBANK => 'Raiffeisebank'
self::TYPE_RAIFFEISENBANK => 'Raiffeisenbank',
self::TYPE_TATRABANKA => 'Tatra banka'
);
// db relations
application/models/bank_transfer.php
return $duplicate_transaction_codes;
}
/**
* Returns duplicities in table for given bank account
* Used in Tatra banka importer
*
* @author David Raska
* @param $bank_account_id
* @return array
*/
public function get_transactions_duplicities($bank_account_id)
{
$duplicities = $this->db->query("
SELECT t.datetime, t.amount, oba.name, CONCAT(oba.account_nr, '/', oba.bank_nr) AS bank_account, COUNT(bt.id) AS count
FROM `bank_transfers` bt
LEFT JOIN `transfers` t ON t.id = bt.transfer_id
LEFT JOIN bank_statements bs ON bs.id = bt.bank_statement_id
LEFT JOIN bank_accounts ba ON ba.id = bs.bank_account_id
LEFT JOIN bank_accounts oba ON oba.id = bt.origin_id
WHERE ba.id = ?
GROUP BY t.datetime, t.amount, bt.origin_id
HAVING count > 1
", $bank_account_id);
$dupl_array = array();
foreach ($duplicities as $duplicity)
{
$dupl_array[] =
$duplicity->datetime . ", " .
$duplicity->amount. " " . Settings::get('currency') . ", " .
__('Origin bank account: ') .
$duplicity->bank_account;
}
return $dupl_array;
}
/**
* Gets last transaction code of the given bank account.

Také k dispozici: Unified diff