v7‰PNG
IHDR Ÿ f Õ†C1 sRGB ®Îé gAMA ±üa pHYs à ÃÇo¨d GIDATx^íÜL”÷ð÷Yçªö("Bh_ò«®¸¢§q5kÖ*:þ0AºšÖ¥]VkJ¢M»¶f¸±8\k2íll£1]q®ÙÔ‚ÆT
PK /\`. . Tax.phpnu [ getDi();
$ret = array();
foreach ($this->getEnabled() as $id)
{
$obj = $this->get($id);
$calcs = $obj->getCalculators($invoice);
if ($calcs && !is_array($calcs))
$calcs = array($calcs);
if ($calcs)
$ret = array_merge($ret, $calcs);
}
return $ret;
}
function getAvailable(){
$result = array();
foreach (get_declared_classes() as $class) {
if (is_subclass_of($class, 'Am_Invoice_Tax'))
$result[] = fromCamelCase(str_replace('Am_Invoice_Tax_', '', $class), '-');
}
return $result;
}
}
/**
* Abstract tax plugin
* @package Am_Invoice
*/
abstract class Am_Invoice_Tax extends Am_Pluggable_Base
{
protected $_idPrefix = 'Am_Invoice_Tax_';
protected $_configPrefix = 'tax.';
protected $_alwaysAbsorb = false; //backward compatability (Am_Invoice_Tax_Gst)
function initForm(HTML_QuickForm2_Container $form) {}
/**
* @param Invoice $invoice
* @return double
*/
function getRate(Invoice $invoice) {}
// get calculators
function getCalculators(Invoice $invoice)
{
$rate = $this->getRate($invoice);
if ($rate > 0.0) {
return ($this->getConfig('absorb') || $this->_alwaysAbsorb) ?
new Am_Invoice_Calc_Tax_Absorb($this->getId(), $this->getConfig('title', $this->getTitle()), $this) :
new Am_Invoice_Calc_Tax($this->getId(), $this->getConfig('title', $this->getTitle()), $this);
}
}
protected function _beforeInitSetupForm()
{
$form = parent::_beforeInitSetupForm();
$form->addText('title', array('class' => 'el-wide'))->setLabel(___('Tax Title'))->addRule('required');
if (!$this->_alwaysAbsorb) {
$form->addAdvCheckbox('absorb')
->setlabel(___('Catalog Prices Include Tax'));
}
return $form;
}
}
class Am_Invoice_Tax_GlobalTax extends Am_Invoice_Tax
{
public function getTitle() { return ___("Global Tax"); }
public function getRate(Invoice $invoice)
{
return $this->getConfig('rate');
}
protected function _initSetupForm(Am_Form_Setup $form)
{
$gr = $form->addGroup()->setLabel(___("Tax Rate\nfor example 18.5 (no percent sign)"));
$gr->addText('rate', array('size'=>5));
$gr->addStatic()->setContent(' %');
}
}
class Am_Invoice_Tax_Regional extends Am_Invoice_Tax
{
public function getTitle()
{
return ___("Regional Tax");
}
public function getRate(Invoice $invoice)
{
$user = $invoice->getUser();
if (!$user) return;
$rate = null;
foreach ((array)$this->getConfig('rate') as $t){
if (!empty($t['zip']))
if (!$this->compareZip($t['zip'], $user->get('zip')))
continue; // no match
if (!empty($t['state']) && ($t['state'] == $user->get('state')) && ($t['country'] == $user->get('country')))
{
$rate = $t['tax_value'];
break;
}
if (!$t['state'] && !empty($t['country']) && ($t['country'] == $user->get('country')))
{
$rate = $t['tax_value'];
break;
}
}
return $rate;
}
protected function compareZip($zipString, $zip)
{
$zip = trim($zip);
foreach (preg_split('/[,;\s]+/', $zipString) as $s)
{
$s = trim($s);
if (!strlen($s)) continue;
if (strpos($s, '-'))
list($range1, $range2) = explode('-', $s);
else
$range1 = $range2 = $s;
if (($range1 <= $zip) && ($zip <= $range2))
{
return true;
}
}
return false;
}
protected function _initSetupForm(Am_Form_Setup $form)
{
$form->addElement(new Am_Form_Element_RegionalTaxes('rate'));
}
}
//class Am_Invoice_Tax_Vat extends Am_Invoice_Tax
//{
// // will be transformed to code => title in constructor
// protected $countries = array(
// 'AT', 'BE', 'BG', 'CY', 'HR', 'CZ', 'DE',
// 'DK', 'EE', 'GR', 'ES', 'FI', 'FR', // GR is known as EL for tax
// 'GB', 'HU', 'IE', 'IT', 'LT', 'LU',
// 'LV', 'MT', 'NL', 'PL', 'PT', 'RO',
// 'SE', 'SK', 'SI'
// );
// protected $euCountries = array();
//
// public function __construct(Am_Di $di, array $config)
// {
// parent::__construct($di, $config);
// $countryList = $di->countryTable->getOptions();
// foreach ($this->countries as $k => $c)
// {
// unset($this->countries[$k]);
// $this->countries[$c] = $countryList[$c];
// }
// }
//
// public function getTitle() { return ___("VAT"); }
//
// public function getRate(Invoice $invoice)
// {
// $u = $invoice->getUser();
// $id = is_null($u) ? false : $u->get('tax_id');
// if ($id && $this->getConfig('extempt_if_vat_number')) {
// // if that is a foreign customer
// if (strtoupper(substr($this->getConfig('my_id'), 0, 2)) != strtoupper(substr($id, 0, 2)))
// return null;
// }
// $country = $id ? substr($id, 0, 2) : ( is_null($u) ? false : $u->get('country'));
// if (!$country) $country = $this->getConfig('my_country');
// if ($country == $this->getConfig('my_country'))
// return $this->getConfig('local_rate');
// if(!$this->getConfig('add_vat_outside_eu') && !array_key_exists(strtoupper($country), $this->countries))
// return null;
// return $this->getConfig('rate.'.strtoupper($country), $this->getConfig('local_rate'));
// }
//
// protected function _initSetupForm(Am_Form_Setup $form)
// {
// $form->addSelect('my_country')
// ->setLabel(___('My Country'))
// ->loadOptions($this->countries)
// ->addRule('required');
//
// $form->addText('my_id')
// ->setLabel(___('VAT Id'))
// ->addRule('required');
//
// $gr = $form->addGroup()
// ->setLabel(___("Local VAT Rate\n" .
// "for example 18.5 (no percent sign)"));
// $gr->addText('local_rate', array('size' => 5, 'maxlength' => 5));
// $gr->addStatic()->setContent(' %');
//
// $form->addAdvCheckbox('extempt_if_vat_number')
// ->setLabel(___('Do not add VAT if a valid EU VAT Id entered by foreign customer'));
//
// $fs = $form->addGroup()
// ->setLabel(___("Tax Rates for Other Countries\n" .
// 'in case of it is different with Local VAT Rate'));
//
// foreach ($this->countries as $k => $v) {
// $fs->addText('rate.' . $k, array('class'=>'vat-rate'));
// }
//
// $rate = $fs->addElement(new Am_Form_Element_VatRate('rate'));
// foreach ($this->countries as $k => $v) {
// $val = $this->getConfig('rate.' . $k, $this->getConfig('local_rate', ''));
// $attr = array(
// 'data-label'=> $v . sprintf(' %%', $val),
// 'data-value' => $val
// );
// $rate->addOption($v, $k, $attr);
// }
//
// $rate->setJsOptions('{
// getOptionName : function (name, option) {
// return name.replace(/\[\]$/, "") + "___" + option.value;
// },
// getOptionValue : function (option) {
// return jQuery(option).data("value");
// }
// }');
// $rate->setValue(array_keys(array_filter($this->getConfig('rate', array()))));
//
// $form->addScript()
// ->setScript(<<addReturn("Important Information about EU VAT Changes: please read");
// }
//
// function onGridUserInitForm(Am_Event_Grid $event)
// {
// $form = $event->getGrid()->getForm();
// $user = $event->getGrid()->getRecord();
//
// $address_fieldset = $form->getElementById('address_info');
//
// $tax_id_el = new HTML_QuickForm2_Element_InputText('tax_id');
// $tax_id_el->setLabel(___('Tax Id'));
//
// $form->insertBefore($tax_id_el, $address_fieldset);
// }
//}
class Am_Invoice_Tax_Vat2015 extends Am_Invoice_Tax
{
protected $rates = array(
'AT'=>20, 'BE' => 21, 'BG'=>20, 'CY'=>19, 'HR'=>25, 'CZ'=>21, 'DE'=>19,
'DK'=>25, 'EE'=>20, 'GR'=>23, 'ES'=>21, 'FI'=>24, 'FR'=>20, // GR is known as EL for tax
'GB'=>20, 'HU'=>27, 'IE'=>23, 'IM'=> 20, 'IT'=>22, 'LT'=>21, 'LU'=>17,
'LV'=>21, 'MT'=>18, 'NL'=>21, 'PL'=>23, 'PT'=>23, 'RO'=>20,
'SE'=>25, 'SK'=>20, 'SI'=>22);
protected $countries = array();
protected $countryLookupService;
const INVOICE_IP = 'tax_invoice_ip';
const INVOICE_IP_COUNTRY = 'tax_invoice_ip_country';
const USER_REGISTRATION_IP = 'tax_user_registration_ip';
const USER_REGISTRATION_IP_COUNTRY = 'tax_user_registration_ip_country';
const USER_COUNTRY = 'tax_user_country';
const SELF_VALIDATION_COUNTRY = 'tax_self_validation_country';
const INVOICE_COUNTRY = 'tax_invoice_country';
public function __construct(Am_Di $di, array $config)
{
parent::__construct($di, $config);
$countryList = $di->countryTable->getOptions();
foreach ($this->rates as $k=>$c)
{
if(!isset($countryList[$k])) continue;
$this->countries[$k] = $countryList[$k];
}
}
public function getTitle() { return ___("EU VAT"); }
function _initSetupForm(Am_Form_Setup $form)
{
$gr = $form->addGroup('')->setLabel(___("Electronically Supplied Service\n" .
'Enable if ALL your products are electronic services.'));
$gr->addAdvCheckbox('tax_digital');
$gr->addHTML()->setHTML(<<Examples of Electronic Service
Website supply, web-hosting, distance maintenance of programmes and equipment;
Supply of software and updating thereof;
Supply of images, text and information and making available of databases;
Supply of music, films and games, including games of chance and gambling games, and of
political, cultural, artistic, sporting, scientific and entertainment broadcast and events;
Supply of distance teaching.
EOT
);
$form->addAdvCheckbox('b2b_vat')
->setLabel(___("Add VAT to all B2B payments\n"
. "Normally aMember will not add VAT to invoice if\n"
. "customer located in another country and specified valid VAT ID\n"
. "With that setting enabled, VAT will be applied in this situation too."
));
$fieldSet = $form->addFieldSet('maxmind', array('id' => 'maxmind'))->setLabel('Location Validation Settings');
$fieldSet->addHTML(null, array('class'=>'no-label'))->setHTML(<<According to new EU VAT rules your are required to collect two pieces of non-conflicting evidence of customer's location country if you are selling Digital (Electronic Service) Products. These two pieces of evidence will be checked on each invoice which has Digital Product included:
Address Contry (so make sure that you have added Address info brick to signup form)
IP Address Country
In order to get country from customer's IP address, aMember uses MaxMind GeoIP2 service. Please signup here in order to get MaxMind user ID and license key.
EOT
);
$fieldSet->addAdvCheckbox('tax_location_validation')
->setLabel(___("Enable Location Validation\n" .
'aMember will require two peices of location evidence before an invoice is created. ' .
'Invoice that fials validation will be blocked and user will receive warning.'));
$fieldSet->addAdvCheckbox('tax_location_validate_all')
->setLabel(___("Validate Location even if Invoice has no VAT \n" .
"Validate All New Invoices (even if invoice has no VAT)\n".
"If unchecked, location will be validated only when \n".
"user selects country inside EU and only if VAT should be applied to invoice.\n".
"Free invoices won't be validated still"
));
$fieldSet->addAdvCheckbox('tax_location_validate_self')
->setLabel(___("Enable Self-Validation\n" .
"If validation failed, user will be able to confirm current location manually\n"
));
$fieldSet->addText('tax_maxmind_user_id')->setLabel(___('MaxMind User ID'));
$fieldSet->addText('tax_maxmind_license')->setLabel(___('MaxMind License'));
$fieldSet = $form->addFieldSet('vat_id')->setLabel(___("Account Information"));
$fieldSet->addSelect('my_country')
->setLabel(___('My Country'))
->loadOptions($this->countries)
->addRule('required');
$fieldSet->addText('my_id')
->setLabel(___('VAT Id'))
->addRule('required');
$fieldSet = $form->addFieldSet('numbering')->setLabel(___('Invoice numbering'));
$fieldSet->addAdvCheckbox("sequential")->setLabel(___("Sequential Receipt# Numbering\n" .
"aMember still creates unique id for invoices, but it will\n" .
"generate PDF receipts for each payment that will be\n".
"available in the member area for customers"));
$form->setDefault('sequential', 1);
$form->setDefault('tax_digital', 1);
$fieldSet->addText('invoice_prefix')->setLabel(___("Receipt# Number Prefix\n" .
"If you change prefix numbers will start over from 1\n"
. "You can use %year% shortcode in invoice number. It will be replaced to actual year\n"
. "For example: INV-%year%-"))->setValue('INV-');
$fieldSet->addText('initial_invoice_number')->setLabel(___('Initial Receipt# Number'))->setValue(1);
$fieldSet->addText('invoice_refund_prefix')->setLabel(___("Refund Receipt# Prefix\n" .
"If you change prefix numbers will start over from 1"
. "You can use %year% shortcode in invoice number. It will be replaced to actual year\n"
. "For example: RFND-%year%-"))->setValue('RFND-');
$fieldSet->addText('initial_invoice_refund_number')->setLabel(___('Initial Receipt# Refund Number'))->setValue(1);
$this->addRatesTable($form, 'rate', ___('Standard VAT Rates, %'));
$groups = $this->getConfig('tax_groups') ?: array();
foreach ($groups as $id => $title) {
$this->addRatesTable($form, $id, $title . ', %', true);
}
$form->addHidden('schedule')
->setId('tax-rate-schedule-data');
$i18n = array(
'date' => ___('Date'),
'tax_rate' => ___('Tax Rate'),
'delete' => ___('delete'),
'add' => ___('schedule change'),
'change_to' => ___('to %s on %s')
);
$form->addEpilog(<<
@media all and (min-width:1100px) {
.tax-rate-container .fieldset .row {
float:left;
width:50%;
}
}
@media all and (min-width:1400px) {
.tax-rate-container .fieldset .row {
float:left;
width:33%;
}
}
.tax-rate-container .fieldset {
background:#f5f5f5;
overflow:hidden;
}
.tax-rate-container .fieldset .row {
background: none;
}
.tax-rate-container.tax-rate-container-add .fieldset .row:first-child {
width:auto;
float:none;
background:#eee;
}
.am-form .tax-rate-container div.element-title label {
font-weight: normal;
}
.tax-rate-group-delete {
color: #ba2727;
}
.tax-rate-schedule-delete {
display: none;
color: #ba2727;
}
.row.tax-rate-has-rule:hover .tax-rate-schedule-delete {
display: inline;
}
.row:hover .tax-rate-schedule {
display:inline;
}
.tax-rate-schedule,
.row.tax-rate-has-rule:hover .tax-rate-schedule {
display: none;
}
.tax-rate-schedule-preview {
display:none;
}
.tax-rate-has-rule .tax-rate-schedule-preview {
color: #c2c2c2;
display: inline;
}
.tax-rate-has-rule.row:hover .tax-rate-schedule-preview {
color: #313131;
}
CUT
);
$form->addScript()
->setScript(<<{$i18n['add']}');
var s = [
'%',
''];
var i =1;
var html = '{$i18n['change_to']}'.replace(/%s/g, function(e) {return s.shift();}, 'g');
$(this).parent().append(' ' + html + ' {$i18n['delete']}');
})
toggleState();
$(document).on('click', '.tax-rate-schedule', function(){
var c = $(this).closest('div').find('.tax-rate').data('country');
var ct = $(this).closest('div').find('.tax-rate').data('country-title');
var id = $(this).closest('div').find('.tax-rate').data('id');
jQuery('#tax-rate-schedule').data('country', c);
jQuery('#tax-rate-schedule').data('id', id);
if (hasSchedule(id, c)) {
d = getSchedule(id, c);
jQuery('#tax-rate-schedule').find('[name=date]').datepicker('setDate', d.date);
jQuery('#tax-rate-schedule').find('[name=rate]').val(d.rate);
} else {
jQuery('#tax-rate-schedule').find('[name=date]').val('');
jQuery('#tax-rate-schedule').find('[name=rate]').val('');
}
jQuery('#tax-rate-schedule').dialog('option', 'title', ct);
jQuery('#tax-rate-schedule').dialog('open');
});
$(document).on('click', '.tax-rate-schedule-delete', function(){
deleteSchedule($(this).closest('.row').find('.tax-rate').data('id'),
$(this).closest('.row').find('.tax-rate').data('country'));
});
jQuery("#tax-rate-schedule").dialog({
buttons: {
'Save' : function() {
var d = _GetSQLDate(jQuery(this).find('[name=date]').datepicker('getDate'));
var r = jQuery(this).find('[name=rate]').val();
setSchedule(jQuery(this).data('id'), jQuery(this).data('country'), {
'date' : d,
'rate' : r});
jQuery(this).dialog("close");
},
'Close' : function() {jQuery(this).dialog("close");}
},
modal : true,
title : '',
autoOpen: false
});
});
CUT
);
$url = $this->getDi()->url('admin-vat-group');
$label = Am_Html::escape(___('Add VAT Rates Group'));
$form->addHtml()
->setHtml('' . $label . '');
}
function addRatesTable($form, $id, $title, $addDeleteLink = false)
{
$fs = $form->addAdvFieldset($id, array(
'id' => 'tax-rate-group-' . $id,
'class' => 'tax-rate-container' . ($addDeleteLink ? ' tax-rate-container-add' : '')))
->setLabel($title);
if ($addDeleteLink) {
$label_del = Am_Html::escape(___('Delete This VAT Rates Group'));
$label_edit = Am_Html::escape(___('Change Title'));
$url_del = $this->getDi()->url('admin-vat-group/delete', array('id' => $id));
$url_edit = $this->getDi()->url('admin-vat-group/edit', array('id' => $id));
$fs->addHtml(null, array('class' => 'no-label'))
->setHtml(<<$label_edit
·
$label_del
CUT
);
}
foreach($this->rates as $c=>$rate){
if(!isset($this->countries[$c])) continue;
$r = $fs->addText("$id.$c", array(
'size' => 3,
'class' => 'tax-rate',
'data-id' => $id,
'data-country' => $c,
'data-country-title' => $this->countries[$c]))
->setLabel($this->countries[$c]);
$r->setValue(!empty($rate[$c])?$rate[$c]:$this->rates[$c]);
}
}
/**
* @return Am_Invoice_CountryLookup_Abstract
*/
function getCountryLookupService()
{
return $this->getDi()->countryLookup;
}
function hasDigitalProducts(Invoice $invoice)
{
if($this->getConfig('tax_digital')) return true;
foreach($invoice->getProducts() as $p){
if($p->tax_group && $p->tax_digital) return true;
}
}
public function getRate(Invoice $invoice, InvoiceItem $item = null)
{
$u = $invoice->getUser();
$id = is_null($u) ? false : $u->get('tax_id');
if ($id && !$this->getConfig('b2b_vat')) {
// if that is a foreign customer
if (strtoupper(substr($this->getConfig('my_id'), 0, 2)) != strtoupper(substr($id, 0, 2)))
return null;
}
$country = $this->hasDigitalProducts($invoice) ? ($id ? substr($id, 0, 2) : ( is_null($u) ? false : $u->get('country'))) : $country = $this->getConfig('my_country');
if (!$country) $country = $this->getConfig('my_country');
//canary islands VAT 0%
if($country == 'ES' && $u && in_array($u->get('state'), array('ES-PM', 'ES-SC')))
return 0;
if ($country == $this->getConfig('my_country'))
return $this->getRatePerProduct($country, $item);
if(!array_key_exists(strtoupper($country), $this->countries))
return null;
return $this->getRatePerProduct($country, $item);
}
function getRatePerProduct($country, InvoiceItem $item=null)
{
$this->schedule(sqlDate('now'));
$country = strtoupper($country);
if (!empty($item) && $item->item_type == 'product') {
if(($product = $item->tryLoadProduct()) && $product->tax_rate_group) {
$rates = $this->getConfig($product->tax_rate_group);
$customRate = (is_array($rates) && isset($rates[$country])&& !empty($rates[$country])) ? $rates[$country] : null;
}
} else {
$customRate = null;
}
return (!is_null($customRate) ? $customRate : $this->getConfig('rate.'.$country, @$this->rates[$country]));
}
function schedule($date)
{
if ($schedule = $this->getConfig('schedule')) {
$schedule = json_decode($schedule, true);
foreach ($schedule as $id => $data) {
foreach ($data as $country => $rule) {
if ($rule['date'] <= $date) {
Am_Config::saveValue("tax.{$this->getId()}.$id.$country", $rule['rate']);
unset($schedule[$id][$country]);
}
}
if (!$schedule[$id]) {
unset($schedule[$id]);
}
}
$schedule = $schedule ? json_encode($schedule) : '';
Am_Config::saveValue("tax.{$this->getId()}.schedule", $schedule);
$this->getDi()->config->read();
$this->_setConfig((array)$this->getDi()->config->get($this->getDi()->plugins_tax->getConfigKey($this->getId())));
}
}
function onDaily(Am_Event $e)
{
$this->schedule(sqlDate('now'));
}
function onGridProductInitForm(Am_Event $event)
{
$form = $event->getGrid()->getForm();
$groups = $this->getConfig('tax_groups') ?: array();
$fieldSet = $form->getElementById('billing');
$fieldSet->addSelect('tax_rate_group')
->setLabel(___('VAT Rates Group'))
->loadOptions(array(
'' => ___('Standard VAT Rates')
) + $groups);
$form->addScript()
->setScript(<<getConfig('tax_digital')) {
$fieldSet = $form->getElementById('billing');
$gr = $fieldSet->addGroup('')->setLabel(___("Electronically Supplied Service\n" .
'Enable if your product is an electronic service.'));
$gr->addAdvCheckbox('tax_digital');
$gr->addHTML()->setHTML(<<Examples of Electronic Service
Website supply, web-hosting, distance maintenance of programmes and equipment;
Supply of software and updating thereof;
Supply of images, text and information and making available of databases;
Supply of music, films and games, including games of chance and gambling games, and of
political, cultural, artistic, sporting, scientific and entertainment broadcast and events;
Supply of distance teaching.
EOT
);
}
}
function onInvoiceValidate(Am_Event $event)
{
$invoice = $event->getInvoice();
$user = $invoice->getUser();
if($user->get('tax_id')) return; // User already has specified his tax ID, do not validate;
// Disable validation for aMember CP;
if(defined('AM_ADMIN') && AM_ADMIN)
return;
if(
(($invoice->first_tax>0) || ($invoice->second_tax>0) || ($this->getConfig('tax_location_validate_all') && ($invoice->first_total>0 || $invoice->second_total>0)))
&& $this->getConfig('tax_location_validation')
&& $this->hasDigitalProducts($invoice)
)
{
if(!$this->locationIsValid($invoice))
$event->setReturn(sprintf(___("Location validation failed.") .
___("Registration country and your IP address country doesn't match. ")
));
}
}
function locationIsValid(Invoice $invoice)
{
$evidence = array();
$user = $invoice->getUser();
$invoice->data()->set(self::INVOICE_IP, $invoice_ip = $this->getDi()->request->getClientIp());
$invoice->data()->set(self::USER_REGISTRATION_IP, $user->remote_addr);
$invoice->data()->set(self::USER_COUNTRY, $user_country = $user->get('country'));
if(!$invoice->data()->get(self::INVOICE_COUNTRY))
$invoice->data()->set(self::INVOICE_COUNTRY, $user_country);
if($this->getConfig('tax_location_validate_self'))
$invoice->data()->set(self::SELF_VALIDATION_COUNTRY, $evidence[] = $user->data()->get(self::SELF_VALIDATION_COUNTRY));
try {
$invoice->data()->set(self::INVOICE_IP_COUNTRY, $evidence[] = $this->getCountryLookupService()->getCountryCodeByIp($invoice_ip));
} catch(Exception $e) {
$this->getDi()->errorLogTable->logException($e);
}
try {
$invoice->data()->set(self::USER_REGISTRATION_IP_COUNTRY, $evidence[] = $this->getCountryLookupService()->getCountryCodeByIp($user->remote_addr));
} catch(Exception $e) {
$this->getDi()->errorLogTable->logException($e);
}
if(!in_array($user_country, $evidence))
return false;
return true;
}
function onSetDisplayInvoicePaymentId(Am_Event $e)
{
if($this->getConfig('sequential'))
$this->setSequentialNumber($e,$this->getConfig('invoice_prefix','INV-'), $this->getConfig('initial_invoice_number', 1));
}
function onSetDisplayInvoiceRefundId(Am_Event $e)
{
if($this->getConfig('sequential'))
$this->setSequentialNumber($e,$this->getConfig('invoice_refund_prefix','RFND-'), $this->getConfig('initial_invoice_refund_number', 1));
}
function setSequentialNumber(Am_Event $e, $prefix, $default)
{
$prefix = str_replace(array('%year%'), array(date('Y')), $prefix);
$numbers = $this->getDi()->store->getBlob('invoice_sequential_numbers');
if(empty($numbers))
$numbers = array();
else
$numbers = @unserialize($numbers);
if(empty($numbers[$prefix]))
$numbers[$prefix] = $default;
else
$numbers[$prefix]++;
$this->getDi()->store->setBlob('invoice_sequential_numbers', serialize($numbers));
$e->setReturn($prefix.$numbers[$prefix]);
}
function getReadme()
{
return <<http://www.amember.com/docs/Configure_EU_VAT
EOT;
}
function onValidateSavedForm(Am_Event $e)
{
if(!$this->getConfig('tax_location_validate_self')) return;
$form = $e->getForm();
$el = $form->getElementById('f_country');
if(!empty($el))
{
$user_country = $el->getValue();
try{
// Form has country element. Now we need to check user's choice and validate it agains IP country.
$current_country = $this->getDi()->countryLookup->getCountryCodeByIp($this->getDi()->request->getClientIp());
}catch(Exception $e){
// Nothing to do;
$error = $e->getMessage();
}
if(empty($current_country) || ($current_country !== $user_country)){
// Need to add self-validation element;
if(!($sve = $form->getElementById('tax_self_validation')) || !$sve->getValue()){
$sve = new Am_Form_Element_AdvCheckbox('tax_self_validation');
$sve->setLabel(___('Confirm Billing Country'))
->setId('tax_self_validation')
->setContent(sprintf(
"".___("I confirm I'm based in %s")."",
$this->getDi()->countryTable->getTitleByCode($user_country)));
foreach($form as $el1){
$form->insertBefore($sve, $el1);
break;
}
if($sve->getValue()){
// Confirmed;
$this->getDi()->session->tax_self_validation_country = $el->getValue();
}else{
$form->setError(
___("Please confirm your billing address manually") . " " .
___("It looks like you are not at home right now.") . " " .
___("In order to comply with EU VAT Rules we need you to confirm your billing country.")
);
}
}else{
// Element already added; nothing to do
}
}
}
}
function setSelfValidationCountry(User $user)
{
if($country = $this->getDi()->session->tax_self_validation_country){
$user->data()->set(self::SELF_VALIDATION_COUNTRY, $country);
$this->getDi()->session->tax_self_validation_country = null;
}
}
function onUserBeforeUpdate(Am_Event $e)
{
$this->setSelfValidationCountry($e->getUser());
}
function onUserBeforeInsert(Am_Event $e)
{
$this->setSelfValidationCountry($e->getUser());
}
function onAdminMenu(Am_Event $e)
{
$menu = $e->getMenu();
$reports = $menu->findOneBy('id', 'reports');
$reports->addPage(array(
'id' => 'reports-vat',
'controller' => 'admin-vat-report',
'label' => ___('EU VAT Report'),
'resource' => Am_Auth_Admin::PERM_REPORT,
));
}
function onGridUserInitForm(Am_Event_Grid $event)
{
$form = $event->getGrid()->getForm();
$address_fieldset = $form->getElementById('address_info');
$tax_id_el = new HTML_QuickForm2_Element_InputText('tax_id');
$tax_id_el->setLabel(___('Tax Id'));
$form->insertBefore($tax_id_el, $address_fieldset);
}
}
class Am_Invoice_Tax_Gst extends Am_Invoice_Tax_Regional
{
protected $_alwaysAbsorb = true;
public function getTitle()
{
return ___("GST (Inclusive Tax)");
}
}PK /\&@" @" Calc.phpnu [ invoiceBill = $invoiceBill;
foreach ($invoiceBill->getItems() as $item)
{
$this->item = $item;
foreach (self::$_prefixes as $prefix)
{
$this->currentPrefix = $prefix;
$fields = new stdClass;
foreach (self::$_noPrefixFields as $k)
$fields->$k = empty($item->$k) ? null : $item->$k;
foreach (self::$_prefixFields as $k)
{
$kk = $prefix ? $prefix .$k : $k;
if (isset($fields->$kk))
throw new Am_Exception_InternalError("Field is already defined [$k]");
$fields->$k = empty($item->$kk) ? 0 : $item->$kk;
}
$this->calculatePiece($fields);
foreach (self::$_noPrefixFields as $k)
$item->$k = $fields->$k;
foreach (self::$_prefixFields as $k)
{
$kk = $prefix ? $prefix . $k : $k;
$item->$kk = $fields->$k;
}
}
unset($this->item);
}
}
}
class Am_Invoice_Calc_Coupon extends Am_Invoice_Calc
{
/** @var Coupon */
protected $coupon;
protected $user;
public function calculate(Invoice $invoiceBill)
{
$this->coupon = $invoiceBill->getCoupon();
$this->user = $invoiceBill->getUser();
$isFirstPayment = $invoiceBill->isFirstPayment();
foreach ($invoiceBill->getItems() as $item) {
$item->first_discount = $item->second_discount = 0;
$item->_calculateTotal();
}
if (!$this->coupon) return;
if ($this->coupon->getBatch()->discount_type == Coupon::DISCOUNT_PERCENT){
foreach ($invoiceBill->getItems() as $item) {
if ($this->coupon->isApplicable($item->item_type, $item->item_id, $isFirstPayment))
$item->first_discount = $item->qty * moneyRound($item->first_price * $this->coupon->getBatch()->discount / 100);
if ($this->coupon->isApplicable($item->item_type, $item->item_id, false))
$item->second_discount = $item->qty * moneyRound($item->second_price * $this->coupon->getBatch()->discount / 100);
}
} else { // absolute discount
$discountFirst = $this->coupon->getBatch()->discount;
$discountSecond = $this->coupon->getBatch()->discount;
$first_discountable = $second_discountable = array();
$first_total = $second_total = 0;
$second_total = array_reduce($second_discountable, function($s, $item) {return $s+=$item->second_total;}, 0);
foreach ($invoiceBill->getItems() as $item) {
if ($this->coupon->isApplicable($item->item_type, $item->item_id, $isFirstPayment)) {
$first_total += $item->first_total;
$first_discountable[] = $item;
}
if ($this->coupon->isApplicable($item->item_type, $item->item_id, false)) {
$second_total += $item->second_total;
$second_discountable[] = $item;
}
}
if ($first_total) {
$k = max(0,min($discountFirst / $first_total, 1)); // between 0 and 1!
foreach ($first_discountable as $item) {
$item->first_discount = moneyRound($item->first_total * $k);
}
}
if ($second_total) {
$k = max(0,min($discountSecond / $second_total, 1)); // between 0 and 1!
foreach ($second_discountable as $item) {
$item->second_discount = moneyRound($item->second_total * $k);
}
}
}
foreach ($invoiceBill->getItems() as $item) {
$item->_calculateTotal();
}
}
}
class Am_Invoice_Calc_Discount extends Am_Invoice_Calc
{
protected $first, $second;
public function __construct($first, $second)
{
$this->first = moneyRound($first);
$this->second = moneyRound($second);
}
public function calculate(Invoice $invoiceBill)
{
if (!$this->first && !$this->second) return;
$discountFirst = $this->first;
$discountSecond = $this->second;
$first_discountable = $second_discountable = array();
$first_total = $second_total = 0;
foreach ($invoiceBill->getItems() as $item) {
$first_total += $item->first_total;
$first_discountable[] = $item;
$second_total += $item->second_total;
$second_discountable[] = $item;
}
if ($first_total) {
$k = max(0,min($discountFirst / $first_total, 1)); // between 0 and 1!
foreach ($first_discountable as $item) {
$item->first_discount += moneyRound($item->first_total * $k);
}
}
if ($second_total) {
$k = max(0,min($discountSecond / $second_total, 1)); // between 0 and 1!
foreach ($second_discountable as $item) {
$item->second_discount += moneyRound($item->second_total * $k);
}
}
foreach ($invoiceBill->getItems() as $item) {
$item->_calculateTotal();
}
}
}
class Am_Invoice_Calc_Shipping extends Am_Invoice_Calc
{
public function calculatePiece(stdClass $fields)
{
$fields->shipping = 0;
}
}
// @todo 2 different taxes
class Am_Invoice_Calc_Tax extends Am_Invoice_Calc
{
/** @var float */
protected $tax_rate = 0.0;
/** @var Am_Invoice_Tax **/
protected $plugin;
public function __construct($tax_id, $tax_title, Am_Invoice_Tax $plugin)
{
$this->tax_id = $tax_id;
$this->tax_type = $tax_title;
$this->plugin = $plugin;
}
public function calculate(Invoice $invoiceBill)
{
$this->invoice = $invoiceBill;
parent::calculate($invoiceBill);
}
public function calculatePiece(stdClass $fields)
{
$this->tax_rate = $this->plugin->getRate($this->invoice,$this->item);
$this->invoiceBill->tax_rate = $this->tax_rate;
if ($fields->tax_group && ($fields->tax_group != IProduct::NO_TAX))
$fields->tax = moneyRound($fields->total * $this->tax_rate / 100);
else
$fields->tax = 0.0;
$fields->total += $fields->tax;
}
}
class Am_Invoice_Calc_Tax_Absorb extends Am_Invoice_Calc_Tax
{
public function calculatePiece(stdClass $fields)
{
$this->tax_rate = $this->plugin->getRate($this->invoice,$this->item);
$this->invoiceBill->tax_rate = $this->tax_rate;
if ($fields->tax_group && ($fields->tax_group != IProduct::NO_TAX)) {
$price = $fields->price;
$fields->price = moneyRound(($fields->discount * ($this->tax_rate/100) + $fields->price * $fields->qty) / ($fields->qty* (1+($this->tax_rate/100))));
$fields->tax = $price * $fields->qty - ($fields->price * $fields->qty - $fields->discount) - $fields->discount;
} else {
$fields->tax = 0.0;
}
}
}
class Am_Invoice_Calc_Total extends Am_Invoice_Calc
{
public function calculate(Invoice $invoiceBill) {
foreach ($invoiceBill->getItems() as $item)
$item->_calculateTotal();
}
}
class Am_Invoice_Calc_Zero extends Am_Invoice_Calc
{
public function calculatePiece(stdClass $fields)
{
$orig_price = $this->item->data()->get('orig_' . $this->currentPrefix . 'price');
$fields->price = $orig_price ? $orig_price : $fields->price;
$fields->tax = $fields->discount = $fields->shipping = 0.0;
$fields->total = moneyRound($fields->price * $fields->qty);
}
}PK /\`. . Tax.phpnu [ PK /\&@" @" e Calc.phpnu [ PK ݰ