OSB enhancements to date

This commit is contained in:
Deon George
2010-11-30 09:41:08 +11:00
parent 8715a2059b
commit ec6a542bc3
478 changed files with 23423 additions and 9309 deletions

View File

@@ -0,0 +1,88 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides OSB exporting capabilities.
*
* @package OSB
* @subpackage Export
* @category Controllers/Admin
* @author Deon George
* @copyright (c) 2010 Open Source Billing
* @license http://dev.osbill.net/license.html
*/
class Controller_Admin_Export extends Controller_TemplateDefault {
protected $control_title = 'Export';
public $secure_actions = array(
'index'=>TRUE,
'export'=>TRUE,
);
/**
* Export plugins must define an export action.
*/
public function action_export() {
if (empty($_POST['plugin']))
$this->request->redirect('admin/export');
$sc = sprintf('Export_%s',$_POST['plugin']);
if (! class_exists($sc))
throw new Kohana_Exception('Export Class doesnt exist for :plugin',array(':plugin'=>$_POST['plugin']));
else
$export = new $sc;
$export->export();
}
/**
* This is the main call to export, providing a list of items to export and
* setting up the page to call the export plugin when submitted.
*/
public function action_index($daysago=30) {
// @todo this should come from a file list
$TBRexportplugins = array('quicken'=>'Export to Quicken');
$payments = ORM::factory('payment')
->export($daysago);
if (count($payments)) {
$output = Form::open(Request::instance()->uri(array('action'=>'export')));
$output .= '<table class="box-left">';
$output .= View::factory('export/payment/header')
->set('plugins',$TBRexportplugins);
$i = 0;
foreach ($payments as $payment) {
$output .= View::factory('export/payment/body')
->set('payment',$payment)
->set('i',$i++%2);
}
$output .= '</table>';
$output .= Form::submit('submit','export');
$output .= Form::close();
Style::add(array(
'type'=>'file',
'data'=>'css/list.css',
));
Block::add(array(
'title'=>_('Payments to Export'),
'body'=>$output,
));
$this->template->content = Block::factory();
# Nothing to export
} else {
SystemMessage::add(array(
'title'=>_('No payments to export'),
'type'=>'info',
'body'=>sprintf(_('There are no payments within the last %s days (since %s) to show.'),
$daysago,date(Kohana::config('osb')->get('date_format'),$daysago*86400+time())),
));
}
}
}
?>

View File

@@ -0,0 +1,22 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides OSB exporting capabilities for OSB.
*
* @package OSB
* @subpackage Export
* @category Helpers
* @author Deon George
* @copyright (c) 2010 Open Source Billing
* @license http://dev.osbill.net/license.html
*/
class Export {
// Name of export plugin.
protected $plugin;
public function __construct() {
$this->plugin = preg_replace('/^'.get_parent_class($this).'_/','',get_class($this));
$this->request = Request::instance();
}
}
?>

View File

@@ -0,0 +1,198 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides OSB exporting capabilities for Quickbooks.
*
* @package OSB
* @subpackage Export/Quicken
* @category Controllers/Admin
* @author Deon George
* @copyright (c) 2010 Open Source Billing
* @license http://dev.osbill.net/license.html
*/
class Export_Quicken extends Export {
public function export() {
if (! empty($_POST['payment_id']) AND count($_POST['payment_id'])) {
$qo = new Quicken;
foreach ($_POST['payment_id'] as $pid) {
$mpo = ORM::factory('payment',$pid);
if ($mpo->loaded()) {
$invoice_ids = array();
foreach ($mpo->payment_item->find_all() as $pio) {
// If our invoice ID is not blank, then the payment was applied to an invoice
if ($pio->invoice->id) {
// Record our invoice IDs for the summary
array_push($invoice_ids,$pio->invoice->id);
$qio = new Quicken_Invoice;
$qio->TRNSID = sprintf('%06s',$pio->invoice->id);
$qio->DATE = date('m/d/Y',$pio->invoice->date_orig);
$qio->MEMO = 'Import from OSB';
$qio->CLEAR = 'N';
$qio->TOPRINT = 'N';
$qio->PAID = 'N';
$qio->ADDR1 = $mpo->account->address1;
$qio->ADDR2 = $mpo->account->address2;
$qio->ADDR3 = sprintf('%s, %s %s',$mpo->account->city,$mpo->account->state,$mpo->account->zip);
// @todo - should be configurable
$qio->TERMS = '7 Days';
// @todo - should be configurable
$qio->INVTITLE = 'Graytech Hosting Invoice';
// @todo - should be configurable
$qio->INVMEMO = 'Thank you for using Graytech Hosting';
$qio->DOCNUM = sprintf('%06s',$pio->invoice->id);
$qio->DUEDATE = date('m/d/Y',$pio->invoice->due_date);
$qio->AMOUNT = sprintf('%3.2f',$pio->invoice->total_amt);
if ($mpo->account->company)
$qio->NAME = $mpo->account->company;
else
$qio->NAME = sprintf('%s %s',$mpo->account->last_name,$mpo->account->first_name);
// Other Quicken fields not used.
#$qio->CLASS = '';
#$qio->SHIPVIA = '';
#$qio->SHIPDATE = '';
#$qio->OTHER1 = '';
#$qio->REP = '';
#$qio->FOB = '';
#$qio->PONUM = '';
#$qio->SADDR1 = '';
#$qio->SADDR2 = '';
#$qio->SADDR3 = '';
#$qio->SADDR4 = '';
#$qio->SADDR5 = '';
// Add the items to the invoice
foreach ($pio->invoice->invoice_item->find_all() as $iio) {
$qto = new Quicken_InvoiceItem;
if ($iio->date_start OR $iio->date_stop)
$daterange = sprintf('%s-%s',date('d-m-Y',$iio->date_start),date('d-m-Y',$iio->date_stop));
elseif ($iio->product_attr && preg_match('/^a/',$iio->product_attr)) {
echo 'Uncaptured';die();
} elseif ($iio->product_attr && preg_match('/^s/',$iio->product_attr))
$daterange = preg_replace("/\r?\n/",' ',unserialize($iio->product_attr));
else
$daterange = '';
if (! $iio->product_id && preg_match('/^DOMAIN/',$iio->sku)) {
$qto->ACCNT = 'Internet:Domain Name';
$qto->INVITEM = sprintf('Domain:%s',
($iio->domain_tld) ? strtoupper($iio->domain_tld) : 'Unknown');
$qto->MEMO = sprintf('Domain: %s.%s (%s)',
strtoupper($iio->domain_name),strtoupper($iio->domain_tld),$daterange);
} elseif ($iio->product_id) {
$module = ORM::factory('module',array('name'=>'product'));
$export = ORM::factory('export','module')
->where('plugin_name','=',$this->plugin)
->and_where('module_id','=',$module->id)
->and_where('item_id','=',$iio->product_id)
->find();
if ($export->loaded()) {
$map_data = unserialize($export->map_data);
$qto->ACCNT = $map_data['account'];
$qto->INVITEM = $map_data['item'];
} else {
$qto->ACCNT = 'Other Income';
$qto->INVITEM = 'Product:Unknown';
}
$qto->MEMO = sprintf('%s (%s)',
$iio->product->product_translate->find()->name,$daterange);
} else {
$qto->ACCNT = 'Other Income';
$qto->INVITEM = 'Unknown';
$qto->MEMO = sprintf('%s (%s)',
$iio->product->product_translate->find()->name,$daterange);
}
$qto->CLEAR = 'N';
$qto->QNTY = -1;
if ($pio->invoice->tax_amt) {
$qto->TAXABLE = 'Y';
# @todo, get this from OSB
$qto->TAXCODE = 'GST';
$qto->TAXRATE = sprintf('%3.2f%%','0.10');
$qto->TAXAMOUNT = sprintf('%3.2f',$iio->tax_amt*-1);
} else {
$qto->TAXAMOUNT = 0;
}
$qto->PRICE = sprintf('%3.2f',$iio->total_amt-$iio->tax_amt);
$qto->AMOUNT = sprintf('%3.2f',($iio->total_amt-$iio->tax_amt)*-1);
$qio->addInvoiceItem($qto);
}
$qo->addInvoice($qio);
}
}
$qpo = new Quicken_Payment;
$qpo->AMOUNT = sprintf('%3.2f',$mpo->total_amt);
$qpo->TRNSID = sprintf('P%06s',$mpo->id);
$qpo->DATE = date('m/d/Y',$mpo->date_payment);
// @todo this should be from a function - when no invoice is paid we cant use $qio
if ($mpo->account->company)
$qpo->NAME = $mpo->account->company;
else
$qpo->NAME = sprintf('%s %s',$mpo->account->last_name,$mpo->account->first_name);
$qpo->CLEAR = 'N';
$qpo->MEMO = sprintf('Payment for invoice(s) %s (%s)',implode(':',$invoice_ids),$mpo->checkout->name);
// @todo Accounts/Payment should be configurable
switch ($mpo->checkout->checkout_plugin) {
// @todo this is direct debit
case 'MANUAL':
$qpo->PAYMETH = 'DirectDebit';
$qpo->ACCNT = 'Ezypay';
break;
case 'REMIT_CHECK':
$qpo->PAYMETH = 'Cheque';
$qpo->ACCNT = 'Undeposited Funds';
break;
case 'REMIT_BANK_WIRE':
$qpo->PAYMETH = 'DirectCredit';
$qpo->ACCNT = 'Bendigo Bank';
break;
case 'PAYPAL':
$qpo->PAYMETH = 'Paypal';
$qpo->ACCNT = 'Paypal';
break;
default:
$qpo->PAYMETH = 'TBA';
$qpo->ACCNT = 'Undeposited Funds';
}
$qio->addPayment($qpo);
}
}
}
if (! empty($qo))
$this->request->response = $qo->export();
$this->request->send_file(TRUE,'quicken-import.iif');
}
}
?>

View File

@@ -0,0 +1,14 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class supports OSB exporting by rending the exportable items.
*
* @package OSB
* @subpackage Export
* @category Models
* @author Deon George
* @copyright (c) 2010 Open Source Billing
* @license http://dev.osbill.net/license.html
*/
class Model_Export extends ORMOSB {
}

View File

@@ -0,0 +1,35 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This is the abstract class for all export capabilities.
*
* @package OSB
* @subpackage Export
* @category Classes/Abstract
* @author Deon George
* @copyright (c) 2010 Open Source Billing
* @license http://dev.osbill.net/license.html
*/
abstract class OSBExport {
private $_data = array();
protected $_items = array();
protected $_payments = array();
abstract public function export();
public function __get($key) {
return $this->_data[$key];
}
public function __set($key,$value) {
$this->_data[$key] = $value;
}
protected function keys() {
return array_keys($this->_data);
}
protected function vals() {
return array_values($this->_data);
}
}

View File

@@ -0,0 +1,66 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides Quicken exporting capabilities.
*
* @package OSB
* @subpackage Export
* @category Classes/Quicken
* @author Deon George
* @copyright (c) 2010 Open Source Billing
* @license http://dev.osbill.net/license.html
*/
class Quicken extends OSBExport {
protected $defaults = array();
protected function keys() {
return array_merge(array_keys($this->defaults),parent::keys());
}
protected function vals() {
return array_merge(array_values($this->defaults),parent::vals());
}
public function addInvoice(Quicken_Invoice $item) {
array_push($this->_items,$item);
}
public function export() {
$export = '';
foreach ($this->_items as $inv) {
# Invoices
$export .= "!TRNS\t";
$export .= implode("\t",$inv->keys())."\n";
$export .= "TRNS\t";
$export .= implode("\t",$inv->vals())."\n";
# Invoice Items
$spl = 0;
foreach ($inv->_items as $invitem) {
if (! $spl) {
$export .= "!SPL\tSPLID\t";
$export .= implode("\t",$invitem->keys())."\n";
}
$export .= sprintf("SPL\t%s\t%s",$spl++,implode("\t",$invitem->vals()))."\n";
}
$export .= "ENDTRNS\n";
# Payments
foreach ($inv->_payments as $payitem) {
$export .= "!TRNS\t";
$export .= implode("\t",$payitem->keys())."\n";
$export .= "TRNS\t";
$export .= implode("\t",$payitem->vals())."\n";
$export .= sprintf("!SPL\t%s\t%s\t%s\t%s\t%s\t",'SPLID','TRANSTYPE','CLEAR','ACCNT','AMOUNT')."\n";
$export .= sprintf("SPL\t%s\t%s\t%s\t%s\t%s\t",0,'PAYMENT','N','Accounts Receivable',$payitem->AMOUNT)."\n";
$export .= "ENDTRNS\n";
}
}
return $export;
}
}

View File

@@ -0,0 +1,26 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides Quicken exporting capabilities.
*
* @package OSB
* @subpackage Export
* @category Classes/Quicken
* @author Deon George
* @copyright (c) 2010 Open Source Billing
* @license http://dev.osbill.net/license.html
*/
class Quicken_Invoice extends Quicken {
protected $defaults = array(
'ACCNT'=>'Accounts Receivable',
'TRNSTYPE'=>'TAX_INVOICE'
);
public function addInvoiceItem(Quicken_InvoiceItem $item) {
array_push($this->_items,$item);
}
public function addPayment(Quicken_Payment $item) {
array_push($this->_payments,$item);
}
}

View File

@@ -0,0 +1,14 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides Quicken exporting capabilities.
*
* @package OSB
* @subpackage Export
* @category Classes/Quicken
* @author Deon George
* @copyright (c) 2010 Open Source Billing
* @license http://dev.osbill.net/license.html
*/
class Quicken_InvoiceItem extends Quicken {
}

View File

@@ -0,0 +1,17 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides Quicken exporting capabilities.
*
* @package OSB
* @subpackage Export
* @category Classes/Quicken
* @author Deon George
* @copyright (c) 2010 Open Source Billing
* @license http://dev.osbill.net/license.html
*/
class Quicken_Payment extends Quicken {
protected $defaults = array(
'TRNSTYPE'=>'PAYMENT'
);
}

View File

@@ -0,0 +1,65 @@
<?php
/**
* osBilling - Open Billing Software
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Originally authored by Deon George
*
* @author Deon George <deonATleenooksDOTnet>
* @copyright 2009 Deon George
* @link http://osb.leenooks.net
* @license http://www.gnu.org/licenses/
* @package AgileBill
* @subpackage Modules:Payment
*/
/**
* The main AgileBill Export Class
*
* @package AgileBill
* @subpackage Modules:Export
*/
class export extends OSB_module {
/**
* @uses module
*/
public function add($VAR) {
if (isset($VAR[$this->table.'_module_id']) && empty($VAR[$this->table.'_map_data'])) {
global $VAR;
$VAR['_page'] = 'export:add';
require_once(PATH_MODULES.'module/module.inc.php');
$mo = new module($VAR[$this->table.'_module_id']);
$VAR['module_name'] = $mo->getRecordAttr('name');
return;
}
parent::add($VAR);
}
public function view($VAR) {
global $smarty;
$s = parent::view($VAR);
if (isset($s['module_id'])) {
require_once(PATH_MODULES.'module/module.inc.php');
$mo = new module($s['module_id']);
$smarty->assign('module_name',$mo->getRecordAttr('name'));
}
}
}
?>

View File

@@ -0,0 +1,122 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<construct>
<!-- Module name -->
<module>export</module>
<!-- Module supporting database table -->
<table>export</table>
<!-- Module dependancy(s) (module wont install if these modules are not yet installed) -->
<dependancy></dependancy>
<!-- DB cache in seconds -->
<cache>0</cache>
<!-- Default order_by field for SQL queries -->
<order_by>plugin_name</order_by>
<!-- Default SQL limit for SQL queries -->
<limit>25</limit>
<!-- Schema version (used to determine if the schema has change during upgrades) -->
<version>1</version>
<!-- Database indexes -->
<index>
</index>
<!-- Database fields -->
<field>
<!-- Record ID -->
<id>
<index>1</index>
<type>I4</type>
<unique>1</unique>
</id>
<!-- Site ID -->
<site_id>
<index>1</index>
<type>I4</type>
</site_id>
<!-- Date record created -->
<date_orig>
<convert>date-now</convert>
<display>Date Created</display>
<type>I8</type>
</date_orig>
<!-- Date record updated -->
<date_last>
<convert>date-now</convert>
<display>Date Updated</display>
<type>I8</type>
</date_last>
<!-- Record active (BOOL)-->
<status>
<display>Active</display>
<type>L</type>
</status>
<!-- Export plugin name -->
<plugin_name>
<display>Plugin</display>
<type>C(128)</type>
<validate>any</validate>
</plugin_name>
<!-- Module name for data translation -->
<module_id>
<display>Module</display>
<type>I4</type>
<validate>numeric</validate>
</module_id>
<!-- Module item id name -->
<item_id>
<display>Item</display>
<type>I4</type>
<validate>numeric</validate>
<unique>1</unique>
</item_id>
<!--Map data for the export -->
<map_data>
<convert>array</convert>
<display>Map Data</display>
<type>C(128)</type>
<validate>any</validate>
</map_data>
</field>
<!-- Methods for this class, and the fields they have access to, if applicable -->
<method>
<add>plugin_name,module_id,item_id,map_data</add>
<delete>id</delete>
<search>id,plugin_name,module_id,item_id,map_data</search>
<search_show>id,plugin_name,module_id,item_id,map_data</search_show>
<view>id,plugin_name,module_id,item_id,map_data</view>
<update>item_id,map_data</update>
</method>
<!-- Method triggers -->
<trigger></trigger>
<!-- Template page display titles -->
<title>
<add>Add Map Item</add>
</title>
<!-- Template helpers -->
<tpl>
<search_show>
<checkbox>
<field>id</field>
<type>checkbox</type>
<width>25px</width>
</checkbox>
<plugin_name>
<field>plugin_name</field>
</plugin_name>
<module_id>
<field>module_id</field>
</module_id>
<item_id>
<field>item_id</field>
</item_id>
<icon>
<field>status</field>
<type>bool_icon</type>
<width>20px</width>
</icon>
</search_show>
</tpl>
</construct>

View File

@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<install>
<!-- Tree Menu Module Properties -->
<module_properties>
<!-- MODULE Dependancy, this module wont be installed if the dependant modules dont exist -->
<dependancy></dependancy>
<!-- Translated display to use on the tree -->
<display>Export</display>
<!-- Display a module in the menu tree -->
<menu_display>1</menu_display>
<!-- MODULE Name -->
<name>export</name>
<!-- MODULE Notes, these notes show up in the modules table, as a description of the module -->
<notes><![CDATA[These notes show up in the modules table.]]></notes>
<!-- MODULE Parent, the parent node in the tree -->
<parent>setup</parent>
<!-- SUB Modules to install with this one -->
<sub_modules></sub_modules>
<!-- MODULE Type (core|base), core modules cannot be deleted, unrecognised types are ignored. -->
<type>base</type>
</module_properties>
<!-- Tree Menu & Module Methods to load, they will be assigned the group permissions on install time, as selected by the user. -->
<module_method>
<add>
<display>Add</display>
<menu_display>1</menu_display>
<name>add</name>
<notes><![CDATA[Add records]]></notes>
</add>
<delete>
<name>delete</name>
<notes><![CDATA[Delete records]]></notes>
</delete>
<search>
<display>List</display>
<menu_display>1</menu_display>
<name>search</name>
<notes><![CDATA[List records]]></notes>
<page><![CDATA[core:search&module=%%&_next_page_one=view]]></page>
</search>
<search_form>
<name>search_form</name>
<notes><![CDATA[Search for records]]></notes>
</search_form>
<search_show>
<name>search_show</name>
<notes><![CDATA[Show the results of a search]]></notes>
</search_show>
<update>
<name>update</name>
<notes><![CDATA[Update a record]]></notes>
</update>
<view>
<name>view</name>
<notes><![CDATA[View a record]]></notes>
</view>
</module_method>
</install>

View File

@@ -0,0 +1,7 @@
<tr class="<?php echo $i ? 'odd' : 'even'; ?>">
<td><?php echo $payment->display('date_payment'); ?></td>
<td><?php printf('%s (%s)',$payment->checkout->name,$payment->checkout->id); ?></td>
<td><?php echo $payment->display('total_amt'); ?></td>
<td><?php echo $payment->account->name(); ?></td>
<td><?php echo Form::checkbox('payment_id[]',$payment->id); ?></td>
</tr>

View File

@@ -0,0 +1,12 @@
<tr>
<td colspan="3"><?php echo Form::select('plugin',$plugins); ?></td>
<td colspan="2" style="text-align: right;"><?php echo Form::submit('submit','export'); ?></td>
</tr>
<!-- // @todo To translate -->
<tr>
<td class="heading">Date</td>
<td class="heading">Method</td>
<td class="heading">Pay/Amount</td>
<td class="heading">Customer</td>
<td class="heading">Export</td>
</tr>

View File

@@ -0,0 +1,28 @@
<?php
echo '<b>'.__FILE__.'</b><br/>';
echo Form::open();
echo Form::select('plugin',$plugins);
echo '<table>';
echo '<tr>';
printf('<td class="heading">%s</td>','Date');
printf('<td class="heading">%s</td>','Invoice');
printf('<td class="heading">%s</td>','Customer');
printf('<td class="heading">%s</td>','Method');
printf('<td class="heading">%s</td>','Export');
echo '</tr>';
$c = 0;
foreach ($invoices as $invoice) {
printf('<tr class="%s">',(++$c%2==0?'even':'odd'));
printf('<td>%s</td><td>%s</td><td>%s %s (%s)</td><td>%s [%s/%s]</td><td><input type="checkbox" name="invoiceid[]" value="%s" /></td>',
$invoice->date_orig,$invoice->id,
$invoice->account->first_name,$invoice->account->last_name,$invoice->account->company,'paymentmethod',$invoice->total_amt,$invoice->due_date,
$invoice->id);
echo '</tr>';
}
echo '</table>';
echo Form::close();
echo '<PRE>';print_r($invoice);
?>