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,75 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides product categories
*
* @package OSB
* @subpackage Page
* @category Controllers
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
*/
class Controller_Product extends Controller_TemplateDefault {
/**
* Show the available topics in a category
* @todo Only show categories according to their validity dates
* @todo Obey sort order
*/
public function action_category($id) {
$cat = ORM::factory('product_category',$id);
if (! $cat->loaded())
Request::instance()->redirect('welcome/index');
Breadcrumb::name($this->request->uri(),$cat->name);
Block::add(array(
'title'=>sprintf('%s: %s',_('Category'),$cat->name),
'body'=>View::factory('product/category/view')
->set('results',$this->_get_category($cat->id))
->set('cat',$cat->id),
));
$this->template->content = Block::factory();
}
/**
* Obtain a list of pages in a category
*/
private function _get_category($id) {
return ORM::factory('product')->category($id);
}
/**
* Show a product
*/
public function action_view($id) {
$po = ORM::factory('product',$id);
if (! $po->loaded())
Request::instance()->redirect('product_category/index');
Breadcrumb::name($this->request->uri(),$po->product_translate->find()->name);
// Work out our category id for the control line
if (! empty($_GET['cid'])) {
$co = ORM::factory('product_category',$_GET['cid']);
// If the product category doesnt exist, or doesnt match the product
if (! $co->loaded() OR ! in_array($co->id,unserialize($po->avail_category_id)))
Request::instance()->redirect('product_category/index');
Breadcrumb::name('product/view',$co->name);
}
Block::add(array(
'title'=>$po->product_translate->find()->description_short,
'body'=>View::factory('product/view')
->set('record',$po),
));
$this->template->content = Block::factory();
}
}
?>

View File

@@ -0,0 +1,38 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides product categories
*
* @package OSB
* @subpackage Page
* @category Controllers
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
*/
class Controller_Product_Category extends Controller_TemplateDefault {
/**
* By default show a menu of available categories
*/
public function action_index() {
Block::add(array(
'title'=>_('Product Categories'),
'body'=>View::factory('product/category/list')
->set('results',$this->_get_categories()),
));
$this->template->content = Block::factory();
}
/**
* Obtain a list of our categories
* @todo Only show categories according to the users group memeberhsip
* @todo Obey sort order
*/
private function _get_categories() {
return ORM::factory('product_category')
->where('status','=',TRUE)
->find_all();
}
}
?>

View File

@@ -0,0 +1,89 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class supports OSB listing products
*
* @package OSB
* @subpackage Product
* @category Models
* @author Deon George
* @copyright (c) 2010 Open Source Billing
* @license http://dev.osbill.net/license.html
*/
class Model_Product extends ORMOSB {
// @todo this doesnt have our site_id when getting the translation
protected $_has_many = array(
'product_translate'=>array(),
);
protected $_sorting = array(
'position'=>'asc',
'sku'=>'asc',
);
protected $_formats = array(
'price_type'=>array('StaticList_PriceType::display'=>array()),
);
/**
* Return the products for a given category
*/
public function category($cat) {
$results = array();
foreach ($this->where('active','=',TRUE)->find_all() as $po) {
if ($c = unserialize($po->avail_category_id) AND in_array($cat,$c))
array_push($results,$po);
}
return $results;
}
/**
* Return the best price to the uesr based on the users group's memberships
* @todo This needs to be tested with more than one price group enabled
*/
public function get_price_array() {
if (! $this->loaded())
throw new Kohana_Exception('Call to :method where no object loaded?',array(':method'=>__METHOD__));
// Figure out our eligable groups
// @todo Need to work out our default groups elsewhere, not in product
// All users are members of the all user group "0"
$groups = array(0);
$pg = unserialize($this->price_group);
if (Auth::instance()->logged_in())
foreach (Auth::instance()->get_user()->group->find_all() as $go)
array_push($groups,$go->id);
// Work out the best price for the user
$price = array();
foreach (unserialize($this->price_group) as $bill_freq => $pg) {
if ($pg['show'])
foreach ($groups as $gid) {
if (! empty($pg[$gid])) {
if (empty($price[$bill_freq]['price_base'])
OR ($pg[$gid]['price_base'] AND $price[$bill_freq]['price_base'] > $pg[$gid]['price_base'])) {
$price[$bill_freq]['price_setup'] = $pg[$gid]['price_setup'];
$price[$bill_freq]['price_base'] = $pg[$gid]['price_base'];
}
}
}
}
return $price;
}
/**
* Test if the product is a TRIAL product
* (price_type == 2)
*
* @return boolean
*/
public function is_trial() {
if ($this->price_type == 2)
return TRUE;
else
return FALSE;
}
}
?>

View File

@@ -0,0 +1,20 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class supports OSB listing products by category.
*
* @package OSB
* @subpackage Product
* @category Models
* @author Deon George
* @copyright (c) 2010 Open Source Billing
* @license http://dev.osbill.net/license.html
*/
class Model_Product_Category extends ORMOSB {
protected $_table_name = 'product_cat';
protected $_sorting = array(
'name'=>'asc',
);
}
?>

View File

@@ -0,0 +1,18 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class product access to product translation.
*
* @package OSB
* @subpackage Product
* @category Models
* @author Deon George
* @copyright (c) 2010 Open Source Billing
* @license http://dev.osbill.net/license.html
*/
class Model_Product_Translate extends ORMOSB {
protected $_belongs_to = array(
'product'=>array(),
);
}
?>

View File

@@ -50,29 +50,30 @@ class product extends OSB_module {
$this->details($VAR);
}
public function details($VAR) { return $this->tmdetails($VAR); }
/**
* Show the product details to the user - used on the order form
*/
public function details($VAR) {
public function tmdetails($VAR) {
global $smarty, $C_auth;
if (empty($VAR['id']))
return false;
$db = &DB();
# Able to view inactive items?
if (! $C_auth->auth_method_by_name('invoice','add'))
$active = '';
else
$active = ' AND active=1';
$result = $db->Execute($sql = sqlSelect($db,'product','*',sprintf('id=::%s::%s',$VAR['id'],$active)));
if (! count($result->RecordCount()))
$result = $this->sql_GetRecords(array('where'=>sprintf('id=%s%s',$VAR['id'],$active)));
if (! count($result))
return false;
$result = array_pop($result);
# Check for group settings
$groups = unserialize($result->fields['group_avail']);
$groups = unserialize($result['group_avail']);
$auth = false;
for ($ii=0; $ii<count($groups); $ii++) {
if ($C_auth->auth_group_by_id($groups[$ii])) {
@@ -82,20 +83,22 @@ class product extends OSB_module {
}
}
if (!$auth)
if (! $auth)
return false;
# Define the DB vars as a Smarty accessible block
$smarty->assign('product',$result->fields);
$smarty->assign('record',$result);
# If trial, get the sku of the trial product:
if($result->fields['price_type'] == '2') {
$trial = $db->Execute(sqlSelect($db,'*','product',sprintf('id=::%s::',$result->fields['price_trial_prod'])));
$smarty->assign('trial',$trial->fields);
if ($result['price_type'] == '2') {
$trial = $this->sql_GetRecords(array('where'=>array('id'=>$result['price_trial_prod'])));
if (count($trial))
$smarty->assign('trial',array_pop($trial));
}
# Get the best price for base, setup, & any attributes:
$this->price_arr($result->fields);
$this->price_arr($result);
$smarty->assign('price',$this->price);
# Get any attributes & attribute pricing:
@@ -323,20 +326,24 @@ class product extends OSB_module {
* @param int $weekday Day of Month for fixed billing
* @param int $week Unused
* @return array
* @todo $temp is a temporary hack to stop the rounding of the dates to the begining of the BILLING_WEEKDAY
*/
public function recurrDates($type,$weekday,$week,$period_date=false) {
public function recurrDates($type,$weekday,$week,$period_date=false,$temp=false) {
# Make the period consistent, eg: Quarterly = Jan-Mar,Apr-Jun; HalfYearly = Jan-Jun,Jul-Dec
$strict = false;
$used_months = 0;
# Round the time integer to a whole day.
if (! $period_date)
$period_date = strtotime('today');
else
$period_date = strtotime(date('Y-m-d',$period_date));
switch ($type) {
# Weekly
case 0:
$period_end = $period_date+(86400*7);
return array('start'=>$period_date,'date'=>$period_date,'end'=>$period_end);
return array('start'=>$period_date,'date'=>$period_date,'end'=>$period_end,'prorate'=>1);
# Monthly
case 1:
@@ -376,6 +383,11 @@ class product extends OSB_module {
return false;
}
if (is_null($weekday) && ! $temp)
$weekday = BILLING_WEEKDAY;
elseif ($temp)
$weekday = date('d',$period_date);
if ($strict && $type > 0 && $type < 5)
$used_months = $inc_months-(($inc_months-(date('n',$period_date)%$inc_months))%$inc_months+1);
@@ -387,7 +399,10 @@ class product extends OSB_module {
$period_end = mktime(0,0,0,date('m',$period_start)+$inc_months,$weekday,date('y',$period_start));
return array('start'=>$period_start,'date'=>$period_date,'end' => $period_end);
$total_time = $period_end-$period_start;
$remain_time = $period_end-$period_date;
return array('start'=>$period_start,'date'=>$period_date,'end'=>$period_end,'prorata'=>round($remain_time/$total_time,4));
}
/**
@@ -398,17 +413,13 @@ class product extends OSB_module {
* @param int $week
* @return float
*/
private function prorate($type,$weekday,$week,$period_start=false) {
public function prorate($type,$weekday,$week,$period_start=false) {
$arr = $this->recurrDates($type,$weekday,$week,$period_start);
if (!$arr)
if (! $arr)
return 0;
$total_time = $arr['end']-$arr['start'];
$remain_time = $arr['end']-$arr['date'];
$percent_remain = ($remain_time/$total_time) ;
return round($percent_remain,4);
else
return $arr['prorata'];
}
/**
@@ -770,5 +781,24 @@ class product extends OSB_module {
$db = new CORE_database;
$db->mass_delete($VAR, $this, "");
}
public function getTranslateField($field) {
static $CACHE = array();
if (! $id = $this->getRecordAttr('id'))
return null;
if (! isset($CACHE[$id][$field])) {
$db = &DB();
$rs = $db->Execute(sqlSelect('product_translate',$field,array('where'=>array('product_id'=>$id))));
if ($rs && $rs->RecordCount() && isset($rs->fields[$field]))
$CACHE[$id][$field] = $rs->fields[$field];
else
$CACHE[$id][$field] = null;
}
return $CACHE[$id][$field];
}
}
?>

View File

@@ -80,27 +80,39 @@
<convert>array</convert>
<arr_force>1</arr_force>
</avail_category_id>
<!-- Is product a hosting product -->
<host>
<display>Hosting</display>
<type>L</type>
</host>
<!-- Hosting server ID -->
<host_server_id>
<display>Hosting Server</display>
<type>I4</type>
</host_server_id>
<host_provision_plugin_data>
<type>X2</type>
<convert>array</convert>
</host_provision_plugin_data>
<!-- Can domains be purchased with this hosting plan? -->
<host_allow_domain>
<display>Allow Domain Purchases with this Hosting Plan?</display>
<type>L</type>
</host_allow_domain>
<!-- Force a purchase of a domain name with this plan -->
<host_allow_host_only>
<display>Allow Purchases without a domain?</display>
<type>L</type>
</host_allow_host_only>
<!-- Provide a discount to a domain name for this product -->
<host_discount_tld>
<type>X2</type>
<convert>array</convert>
<display>Discount the following TLDs</display>
<type>X2</type>
</host_discount_tld>
<!-- Amount of discount for the domain name -->
<host_discount_tld_amt>
<display>Domain Purchase Discount Amount</display>
<type>F</type>
</host_discount_tld_amt>
<discount>
@@ -109,6 +121,7 @@
<discount_amount>
<type>F</type>
</discount_amount>
<!-- 1 is RECURRING product,2 is a TRIAL product -->
<price_type>
<type>I4</type>
</price_type>
@@ -213,11 +226,11 @@
<!-- Methods for this class, and the fields they have access to, if applicable -->
<method>
<add>id,site_id,date_orig,date_last,sku,taxable,active,group_avail,position,cart_multiple,avail_category_id,discount,discount_amount,price_type,price_base,price_setup,price_group,price_recurr_type,price_recurr_weekday,price_recurr_week,price_recurr_schedule,price_recurr_cancel,price_trial_length_type,price_trial_length,price_trial_prod,assoc_req_prod,assoc_req_prod_type,assoc_grant_prod,assoc_grant_group,assoc_grant_group_type,assoc_grant_group_days,thumbnail,price_recurr_default,price_recurr_modify,modify_waive_setup,modify_product_arr</add>
<update>id,site_id,date_orig,date_last,sku,taxable,active,group_avail,position,cart_multiple,avail_category_id,discount,discount_amount,price_type,price_base,price_setup,price_group,price_recurr_type,price_recurr_weekday,price_recurr_week,price_recurr_schedule,price_recurr_cancel,price_trial_length_type,price_trial_length,price_trial_prod,assoc_req_prod,assoc_req_prod_type,assoc_grant_prod,assoc_grant_group,assoc_grant_group_type,assoc_grant_group_days,thumbnail,price_recurr_default,host,host_server_id,host_provision_plugin_data,host_allow_domain,host_allow_host_only,host_discount_tld,host_discount_tld_amt,prod_plugin,prod_plugin_file,prod_plugin_data,price_recurr_modify,modify_waive_setup,modify_product_arr</update>
<delete>id,site_id</delete>
<view>id,site_id,date_orig,date_last,sku,taxable,active,group_avail,position,cart_multiple,avail_category_id,discount,discount_amount,price_type,price_base,price_setup,price_group,price_recurr_type,price_recurr_weekday,price_recurr_week,price_recurr_schedule,price_recurr_cancel,price_trial_length_type,price_trial_length,price_trial_prod,assoc_req_prod,assoc_req_prod_type,assoc_grant_prod,assoc_grant_group,assoc_grant_group_type,assoc_grant_group_days,thumbnail,price_recurr_default,host,host_server_id,host_provision_plugin_data,host_allow_domain,host_allow_host_only,host_discount_tld,host_discount_tld_amt,prod_plugin,prod_plugin_file,prod_plugin_data,price_recurr_modify,modify_waive_setup,modify_product_arr</view>
<search>id,site_id,date_orig,date_last,sku,taxable,active,group_avail,position,cart_multiple,avail_category_id,price_type,price_recurr_schedule,price_recurr_cancel,price_base,price_setup</search>
<add>sku,taxable,active,group_avail,avail_category_id,discount,price_type,price_base,price_setup,price_group,price_recurr_type,price_recurr_weekday,price_recurr_week,price_recurr_default</add>
<update>id,date_last,sku,taxable,active,group_avail,position,cart_multiple,avail_category_id,discount,discount_amount,price_type,price_base,price_setup,price_group,price_recurr_type,price_recurr_weekday,price_recurr_week,price_recurr_schedule,price_recurr_cancel,price_trial_length_type,price_trial_length,price_trial_prod,assoc_req_prod,assoc_req_prod_type,assoc_grant_prod,assoc_grant_group,assoc_grant_group_type,assoc_grant_group_days,thumbnail,price_recurr_default,host,host_server_id,host_provision_plugin_data,host_allow_domain,host_allow_host_only,host_discount_tld,host_discount_tld_amt,prod_plugin,prod_plugin_file,prod_plugin_data,price_recurr_modify,modify_waive_setup,modify_product_arr</update>
<delete>id</delete>
<view>id,date_orig,date_last,sku,taxable,active,group_avail,position,cart_multiple,avail_category_id,discount,discount_amount,price_type,price_base,price_setup,price_group,price_recurr_type,price_recurr_weekday,price_recurr_week,price_recurr_schedule,price_recurr_cancel,price_trial_length_type,price_trial_length,price_trial_prod,assoc_req_prod,assoc_req_prod_type,assoc_grant_prod,assoc_grant_group,assoc_grant_group_type,assoc_grant_group_days,thumbnail,price_recurr_default,host,host_server_id,host_provision_plugin_data,host_allow_domain,host_allow_host_only,host_discount_tld,host_discount_tld_amt,prod_plugin,prod_plugin_file,prod_plugin_data,price_recurr_modify,modify_waive_setup,modify_product_arr</view>
<search>id,date_orig,date_last,sku,taxable,active,group_avail,position,cart_multiple,avail_category_id,price_type,price_recurr_schedule,price_recurr_cancel,price_base,price_setup</search>
</method>
<!-- Method triggers -->

View File

@@ -17,7 +17,7 @@
<!-- 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>
<type></type>
</module_properties>
<!-- Tree Menu & Module Methods to load, they will be assigned the group permissions on install time, as selected by the user. -->

View File

@@ -0,0 +1,7 @@
<table width="100%" border="0">
<?php foreach ($results as $value) {?>
<tr>
<td class="menu"><a href="<?echo URL::site('product/category/'.$value->id);?>"><?php echo $value->name?></a></td>
</tr>
<?}?>
</table>

View File

@@ -0,0 +1,40 @@
<?php
if (! count($results)) {
echo _('Sorry, no pages were found in this category, or your account is not authorized for this category.');
return;
}
foreach ($results as $record) {
// Load the language
$translate = $record->product_translate->where('product_id','=',$record->id)->and_where('language_id','=','fr')->find();
// If there isnt a translated page, show the default language
// @todo - default language should come from configuration
if (! $translate->loaded())
$translate = $record->product_translate->where('product_id','=',$record->id)->and_where('language_id','=','en')->find();
?>
<table width="100%" border="0" cellspacing="0" cellpadding="0" class="table_background">
<tr>
<td>
<table width="100%" border="0" cellspacing="1" cellpadding="0">
<tr valign="top">
<td class="heading"><a href="<?php echo URL::site(sprintf('product/view/%s?cid=%s',$record->id,$cat));?>"><?php echo $translate->name; ?></a> (<?php echo Currency::display($record->price_base); ?>/mth)</td>
</tr>
<tr>
<td style="background-color: #FFFFFF;">
<table width="100%" border="0" cellpadding="4">
<tr>
<td class="body"><?php echo $translate->description_short; ?></td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
<br/>
<?php
}
?>

View File

@@ -0,0 +1,85 @@
<?php
if (! $record) {
echo _('Sorry, product not found, or your account is not authorized to view it.');
return;
}
// Load the language
$translate = $record->product_translate->where('product_id','=',$record->id)->and_where('language_id','=','fr')->find();
// If there isnt a translated page, show the default language
// @todo - default language should come from configuration
if (! $translate->loaded())
$translate = $record->product_translate->where('product_id','=',$record->id)->and_where('language_id','=','en')->find();
// @todo Need to add in product attributes
// @todo Need to add in product plugins
echo Form::open('cart/add');
?>
<table width="100%" border="0" cellspacing="0" cellpadding="0" class="table_background">
<tr>
<td>
<?php echo Form::hidden('product_id',$record->id); ?>
<?php echo Form::hidden('quantity',1); ?>
<table width="100%" border="0" cellspacing="1" cellpadding="0">
<tr>
<td style="background-color: #FFFFFF;">
<table width="100%" border="0" cellpadding="4">
<tr>
<td class="body"><?php echo $translate->description_full; ?></td>
<?php if ($record->prod_plugin && method_exists($record->prod_plugin_file,'product_view')) {
$pio = new $record->prod_plugin_file;
echo '<td style="vertical-align: top;">'.$pio->product_view($record->prod_plugin_data).'</td>';
} ?>
</tr>
<tr>
<td class="spacer">&nbsp;</td>
<?php if ($record->prod_plugin && method_exists($record->prod_plugin_file,'contract_view')) {
$pio = new $record->prod_plugin_file;
echo '<td style="vertical-align: top;">'.$pio->contract_view($record->prod_plugin_data,$record->price_base,$record->price_setup).'</td>';
} ?>
</tr>
<tr>
<td class="spacer" colspan="2">&nbsp;</td>
</tr>
<tr>
<td class="body" style="width: 50%;"><b>SKU</b></td>
<td class="body" style="width: 50%;"><b>Currency</b></td>
</tr>
<tr>
<td class="body"><?php echo $record->sku;?></td>
<!-- @todo The default currency should be system configurable and displayed by default -->
<!-- @todo If CURRENCY's value is not active in the DB, then the wrong flag is shown, as StaticList_Module::form() only returns active values -->
<!-- @todo Currency is not used in the cart? -->
<?php $CURRENCY_ISO='AUD'; $CURRENCY=6; $COUNTRY=61?>
<td class="body"><?php echo StaticList_Module::form('currency','currency',$CURRENCY,'id','name',array('status'=>'=:1'));?> <?php echo Country::icon($COUNTRY);?></td>
</tr>
<tr>
<td class="body"><b>Price Type</b></td>
<td class="body"><b>Taxable</b></td>
</tr>
<tr>
<td class="body"><?php echo StaticList_PriceType::display($record->price_type);?></td>
<td class="body"><?php echo StaticList_YesNo::display($record->taxable);?></td>
</tr>
<tr>
<td class="body"><b>Recurring Billing Schedule</b></td>
<td class="body"><b>&nbsp;</b></td>
</tr>
<tr>
<td class="body"><?php echo StaticList_RecurSchedule::form('recurr_schedule',$record);?> </td>
<td class="body"><?php echo '';?></td>
</tr>
</table>
</td>
</tr>
<tr>
<td style="text-align: center;"><?php echo Form::submit('submit','Add to Cart'); ?> | <?php echo Form::submit('submit','Add to Cart & Checkout',array('disabled'=>'disabled')); ?></td>
</tr>
</table>
</td>
</tr>
</table>
<?php echo Form::close(); ?>
<br/>