Jump to content
  • 0

Stuck When Importing.


Kangaroo

Question

importStaff
-----------------
importStaff took: 0.0122 seconds
-----------------

importClients
-----------------
importClients took: 2.9123 seconds
-----------------

importContacts
-----------------
importContacts took: 0.0071 seconds
-----------------

importTaxes
-----------------
importTaxes took: 0.0005 seconds
-----------------

importCurrencies
-----------------
importCurrencies took: 0.0077 seconds
-----------------

importInvoices
-----------------
importInvoices took: 1.7244 seconds
-----------------

importTransactions
-----------------
importPackages
-----------------
importPackageOptions
-----------------
importServices
-----------------
importSupportDepartments
-----------------
importSupportTickets
-----------------
importMisc
-----------------
decrypted 0 values using WHMCS' custom algorithm
decrypt took: 0 seconds
total time took: 9.6978 seconds
Array
(
    [error] => Array
        (
            [0] => The import completed but the following errors ocurred:
            [1] => importTransactions: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'currency' cannot be null on line 124
            [2] => importPackages: There is already an active transaction on line 163
            [3] => importPackageOptions: There is already an active transaction on line 163
            [4] => importServices: There is already an active transaction on line 163
            [5] => importSupportDepartments: There is already an active transaction on line 163
            [6] => importSupportTickets: There is already an active transaction on line 163
            [7] => importMisc: There is already an active transaction on line 163
        )

)

I've tried the SQL checks Cody provided in: http://www.blesta.com/forums/index.php?/topic/3426-whmcs-import-error-option-pricing-id-cannot-be-nul-migration-from-whmcs-5310-to-blesta-32/?hl=option_pricing_id

 

And also edited the importer to change:

$pricing = $this->getOptionPricing($this->WhmcsServices->getTerm($option->billingcycle), $value_id, $currency);

To:

$pricing = $this->getOptionPricing($this->WhmcsServices->getTerm($option->billingcycle), $value_id, $currency);
        if (!$pricing) {
            continue;
        }

No luck.

Link to comment
Share on other sites

7 answers to this question

Recommended Posts

  • 0

Struggled like a litle B* but  F* you whmcs :D here's the file used if anyone else has this issue.

<?php
/**
 * Generic WHMCS Migrator
 *
 * @package blesta
 * @subpackage blesta.plugins.import_manager.components.migrators.whmcs
 * @copyright Copyright (c) 2010, Phillips Data, Inc.
 * @license http://www.blesta.com/license/'>http://www.blesta.com/license/ The Blesta License Agreement
 * @link http://www.blesta.com/ Blesta
 */
class WhmcsMigrator extends Migrator {
	/**
	 * @var array An array of settings
	 */
	protected $settings;
	/**
	 * @var boolean True to fetch all records instead of looping through PDOStatement
	 */
	protected $fetchall = false;
	/**
	 * @var boolean Enable/disable debugging
	 */
	protected $enable_debug = false;
	/**
	 * @var string The default country
	 */
	private $default_country = "US";
	/**
	 * @var string The default first name
	 */
	private $default_firstname = "unknown";
	/**
	 * @var string The default last name
	 */
	private $default_lastname = "unknown";
	/**
	 * @var array An array of credits
	 */
	private $credits = array();
    
    private $default_currency = "AUD";
    
	/**
	 * Runs the import, sets any Input errors encountered
	 */
	public function import() {
		Loader::loadModels($this, array("Companies"));
		
		Configure::set("Whmcs.import_fetchall", false);
		
		if (Configure::get("Whmcs.import_fetchall")) {
			$this->fetchall = true;
			ini_set("memory_limit", "512M");
		}

		$actions = array(
			"importStaff", // works
			"importClients", // works
			"importContacts", // works
			"importTaxes", // works
			"importCurrencies", // works
			"importInvoices", // works
			"importTransactions", // works
			"importPackages", // works
			"importPackageOptions", // works
			"importServices", // works
			"importSupportDepartments", // works
			"importSupportTickets", // works
			"importMisc" // works
		);
        
        // I hate WHMCS!!!!!!!!

		$errors = array();
		$this->startTimer("total time");
		$this->decrypt_count = 0;
		$this->startTimer("decrypt");
		$this->pauseTimer("decrypt");
		foreach ($actions as $action) {

			try {
				// Only import packages if no mappings exist
				if ($action == "importPackages" && isset($this->mappings['packages']))
					continue;
				
				$this->debug($action);
				$this->debug("-----------------");
				$this->startTimer($action);
				$this->{$action}();
				$this->endTimer($action);
				$this->debug("-----------------\n");
			}
			catch (Exception $e) {
				$errors[] = $action . ": " . $e->getMessage() . " on line " . $e->getLine();
			}
		}
		
		if (!empty($errors)) {
			array_unshift($errors, Language::_("Whmcs5_2.!error.import", true));
			$this->Input->setErrors(array('error' => $errors));
		}
		$this->debug("decrypted " . $this->decrypt_count . " values using WHMCS' custom algorithm");
		$this->endTimer("decrypt");
		$this->endTimer("total time");
		
		if ($this->enable_debug) {
			$this->debug(print_r($this->Input->errors(), true));
			exit;
		}
	}
	
	
	/**
	 * Import staff
	 */
	protected function importStaff() {
		Loader::loadModels($this, array("StaffGroups"));
		Loader::loadModels($this, array("Users"));
		$this->loadModel("WhmcsAdmins");
		
		// Create "Support" staff group (no permissions)
		$staff_group = array(
			'company_id' => Configure::get("Blesta.company_id"),
			'name' => "Support",
			'permission_group' => array(),
			'permission' => array()
		);
		$this->StaffGroups->add($staff_group);
	
		$staff_groups = $this->StaffGroups->getAll(Configure::get("Blesta.company_id"));
		
		$groups = array();
		foreach ($staff_groups as $group) {
			if ($group->name == "Administrators") {
				$groups[0] = $group->id;
				$groups[1] = $group->id;
			}
			elseif ($group->name == "Billing") {
				$groups[2] = $group->id;
			}
			elseif ($group->name == "Support") {
				$groups[3] = $group->id;
			}
		}
		
		$admins = $this->fetchall ? $this->WhmcsAdmins->get()->fetchAll() : $this->WhmcsAdmins->get();
		foreach ($admins as $admin) {
			$this->Users->begin();

			// Set aside assigned support departments
			$this->mappings['admin_departs'][$admin->id] = $admin->supportdepts;

			try {
				$user_id = $this->createUser(array(
					'username' => $this->decode($admin->username),
					'password' => $admin->password,
					'date_added' => $this->Users->dateToUtc(date("c"))
				));
				
				$vars = array(
					'user_id' => $user_id,
					'first_name' => $this->decode($admin->firstname),
					'last_name' => $this->decode($admin->lastname),
					'email' => $this->decode($admin->email),
					'status' => $admin->disabled == "0" ? "active" : "inactive",
					'groups' => isset($groups[$admin->roleid]) ? array($groups[$admin->roleid]) : null
				);
				
				$staff_id = $this->addStaff($vars, $admin->id);
				
				if ($staff_id)
					$this->Users->commit();
				else
					$this->Users->rollback();
			}
			catch (Exception $e) {
				$this->local->reset();
				$this->Users->rollback();
			}
		}
		unset($admins);
	}
	
	/**
	 * Import clients
	 */	
	protected function importClients() {
		Loader::loadModels($this, array("Accounts", "Clients", "ClientGroups"));
		$this->loadModel("WhmcsClients");
		
		// Initialize crypto (AES in ECB)
		Loader::loadComponents($this, array("Security"));
		$aes = $this->Security->create("Crypt", "AES", array(1)); // 1 = CRYPT_AES_MODE_ECB
		$aes->disablePadding();
		
		// Set default client group
		$client_groups = $this->ClientGroups->getAll(Configure::get("Blesta.company_id"));
		$this->mappings['client_groups'][0] = $client_groups[0]->id;
		
		// Import client groups
		$groups = $this->fetchall ? $this->WhmcsClients->getGroups()->fetchAll() : $this->WhmcsClients->getGroups();
		foreach ($groups as $group) {
			$group_id = $this->ClientGroups->add(array(
				'name' => $this->decode($group->groupname),
				'company_id' => Configure::get("Blesta.company_id"),
				'color' => str_replace("#", "", $group->groupcolour)
			));
			
			$this->mappings['client_groups'][$group->id] = $group_id;
		}
		unset($groups);
		
		// Import clients
		$clients = $this->fetchall ? $this->WhmcsClients->get()->fetchAll() : $this->WhmcsClients->get();
		$this->local->begin();
		foreach ($clients as $client) {
			
			// Create user
			$user_id = null;
			try {
				$user_id = $this->createUser(array(
					'username' => $this->decode($client->email),
					'password' => $client->password,
					'date_added' => $client->datecreated
				));
			}
			catch (Exception $e) {
				$this->local->reset();
			}
			if (!$user_id)
				continue;
			
			// Create client
			$vars = array(
				'id_format' => "{num}",
				'id_value' => $client->id,
				'user_id' => $user_id,
				'client_group_id' => $this->mappings['client_groups'][$client->groupid],
				'status' => strtolower($client->status) == "closed" ? "inactive" : "active"
			);
			$this->local->insert("clients", $vars);
			$client_id = $this->local->lastInsertId();
			
			$this->mappings['clients'][$client->id] = $client_id;
			
			// Create primary contact
			$vars = array(
				'client_id' => $client_id,
				'contact_type' => "primary",
				'first_name' => $this->decode(trim($client->firstname) != "" ? $client->firstname : $this->default_firstname),
				'last_name' => $this->decode(trim($client->lastname) != "" ? $client->lastname : $this->default_lastname),
				'company' => $this->decode($client->companyname != "" ? $client->companyname : null),
				'email' => $this->decode($client->email),
				'address1' => $this->decode($client->address1),
				'address2' => $this->decode($client->address2 != "" ? $client->address2 : null),
				'city' => $this->decode($client->city),
				'state' => $client->state != "" ? substr($client->state, 0, 3) : null,
				'zip' => $this->decode($client->postcode != "" ? $client->postcode : null),
				'country' => $client->country != "" ? $client->country : $this->default_country,
				'date_added' => $this->Companies->dateToUtc($client->datecreated)
			);
			$this->local->insert("contacts", $vars);
			$contact_id = $this->local->lastInsertId();
			$this->mappings['primary_contacts'][$client->id] = $contact_id;

			// Save client settings
			$settings = array(
				'autodebit' => $client->disableautocc == "on" ? "false" : "true",
				'autosuspend' => "true",
				'default_currency' => $client->currency_code,
				'inv_address_to' => $contact_id,
				'inv_method' => "email",
				'language' => "en_us",
				'tax_exempt' => $client->taxexempt == "on" ? "true" : "false",
				'tax_id' => null,
				'username_type' => "email"
			);
			$this->Clients->setSettings($client_id, $settings);
			
			// Add contact phone number
			if ($client->phonenumber != "") {
				$vars = array(
					'contact_id' => $contact_id,
					'number' => $this->decode($client->phonenumber),
					'type' => "phone",
					'location' => "home"
				);
				$this->local->insert("contact_numbers", $vars);
			}
			
			$aes->setKey($this->mysqlAesKey(md5($this->settings['key'] . $client->id)));
			
			if ($client->cardnum != "")
				$client->cardnum = $aes->decrypt($client->cardnum);
			if ($client->expdate != "")
				$client->expdate = $aes->decrypt($client->expdate);
			if ($client->bankacct != "")
				$client->bankacct = $aes->decrypt($client->bankacct);
			if ($client->bankcode != "")
				$client->bankcode = $aes->decrypt($client->bankcode);
				
			// Add the payment account
			if ($client->cardnum != "") {
				$vars = array(
					'contact_id' => $this->mappings['primary_contacts'][$client->id],
					'first_name' => $this->decode(trim($client->firstname) != "" ? $client->firstname : $this->default_firstname),
					'last_name' => $this->decode(trim($client->lastname) != "" ? $client->lastname : $this->default_lastname),
					'address1' => $this->decode($client->address1 != "" ? $client->address1 : null),
					'address2' => $this->decode($client->address2 != "" ? $client->address2 : null),
					'city' => $this->decode($client->city != "" ? $client->city : null),
					'state' => $this->decode($client->state != "" ? $client->state : null),
					'zip' => $this->decode($client->postcode != "" ? $client->postcode : null),
					'country' => $client->country != "" ? $client->country : $this->default_country,
					'number' => $client->cardnum,
					'expiration' => "20" . substr($client->expdate, 2, 2) . substr($client->expdate, 0, 2)
				);
				
				$account_id = $this->Accounts->addCc($vars);
				
				// Set account for autodebit
				if ($account_id) {
					$vars = array(
						'client_id' => $this->mappings['clients'][$client->id],
						'account_id' => $account_id,
						'type' => "cc"
					);
					$this->local->insert("client_account", $vars);
				}
			}
		}
		$this->local->commit();
		unset($clients);
		
		// Import custom client fields
		$custom_fields = $this->WhmcsClients->getCustomFields()->fetchAll();
		$this->local->begin();
		foreach ($custom_fields as $custom_field) {
			// Add each field to each client group
			foreach ($this->mappings['client_groups'] as $remote_group_id => $group_id) {
				$vars = array(
					'client_group_id' => $group_id,
					'name' => $this->decode($custom_field->fieldname),
					'type' => $this->getFieldType($this->decode($custom_field->fieldtype)),
					'values' => $this->getFieldValues($this->decode($custom_field->fieldoptions)),
					'regex' => $this->decode($custom_field->regexpr != "" ? $custom_field->regexpr : null),
					'show_client' => $custom_field->adminonly == "on" ? "0" : "1"
				);
				$this->local->insert("client_fields", $vars);
				$this->mappings['client_fields'][$custom_field->id][$remote_group_id] = $this->local->lastInsertId();
			}
			
			// Insert custom client values for this field
			$custom_values = $this->fetchall ? $this->WhmcsClients->getCustomFieldValues($custom_field->id)->fetchAll() : $this->WhmcsClients->getCustomFieldValues($custom_field->id);
			foreach ($custom_values as $custom_value) {
				if (!isset($this->mappings['clients'][$custom_value->relid]))
					continue;
				
				$vars = array(
					'client_field_id' => $this->mappings['client_fields'][$custom_field->id][$custom_value->groupid],
					'client_id' => $this->mappings['clients'][$custom_value->relid],
					'value' => $this->decode($custom_value->value)
				);
				$this->local->duplicate("value", "=", $vars['value'])->insert("client_values", $vars);
			}
			unset($custom_values);
		}
		$this->local->commit();
		
		// Import client notes
		$notes = $this->fetchall ? $this->WhmcsClients->getNotes()->fetchAll() : $this->WhmcsClients->getNotes();
		$this->local->begin();
		foreach ($notes as $note) {
			if (!isset($this->mappings['clients'][$note->userid]))
				continue;
			
			$note->note = $this->decode($note->note);
			$title = wordwrap($note->note, 32, "\n", true);
			if (strpos($title, "\n") > 0)
				$title = substr($title, 0, strpos($title, "\n"));
			
			$vars = array(
				'client_id' => $this->mappings['clients'][$note->userid],
				'staff_id' => isset($this->mappings['staff'][$note->adminid]) ? $this->mappings['staff'][$note->adminid] : 0,
				'title' => $title,
				'description' => trim($title) == trim($note->note) ? null : $note->note,
				'stickied' => $note->sticky ? 1 : 0,
				'date_added' => $this->Companies->dateToUtc($note->created),
				'date_updated' => $this->Companies->dateToUtc($note->modified)
			);
			$this->local->insert("client_notes", $vars);
		}
		$this->local->commit();
		unset($notes);
	}
	
	/**
	 * Import contacts
	 */
	protected function importContacts() {
		$this->loadModel("WhmcsContacts");
		
		$contacts = $this->fetchall ? $this->WhmcsContacts->get()->fetchAll() : $this->WhmcsContacts->get();
		$this->local->begin();
		foreach ($contacts as $contact) {
			$vars = array(
				'client_id' => $this->mappings['clients'][$contact->userid],
				'contact_type' => "billing",
				'first_name' => $this->decode($contact->firstname),
				'last_name' => $this->decode($contact->lastname),
				'company' => $this->decode($contact->companyname != "" ? $contact->companyname : null),
				'email' => $this->decode($contact->email),
				'address1' => $this->decode($contact->address1 != "" ? $contact->address1 : null),
				'address2' => $this->decode($contact->address2 != "" ? $contact->address2 : null),
				'city' => $this->decode($contact->city != "" ? $contact->city : null),
				'state' => $this->decode($contact->state != "" ? substr($contact->state, 0, 3) : null),
				'zip' => $this->decode($contact->postcode != "" ? $contact->postcode : null),
				'country' => $contact->country != "" ? $contact->country : $this->default_country,
				'date_added' => $this->Companies->dateToUtc(date("c"))
			);
			$this->local->insert("contacts", $vars);
			$contact_id = $this->local->lastInsertId();
			
			$this->mappings['contacts'][$contact->id] = $contact_id;
			
			// Add contact phone number
			if ($contact->phonenumber != "") {
				$vars = array(
					'contact_id' => $contact_id,
					'number' => $this->decode($contact->phonenumber),
					'type' => "phone",
					'location' => "home"
				);
				$this->local->insert("contact_numbers", $vars);
			}
		}
		$this->local->commit();
		unset($contacts);
	}
	
	/**
	 * Import taxes
	 */
	protected function importTaxes() {
		$this->loadModel("WhmcsTaxes");
		
		$taxes = $this->fetchall ? $this->WhmcsTaxes->get()->fetchAll() : $this->WhmcsTaxes->get();
		$this->local->begin();
		foreach ($taxes as $tax) {
			$state = $this->local->select()->from("states")->
				where("country_alpha2", "=", $tax->country)->
				where("name", "=", trim($tax->state))->fetch();
			
			$vars = array(
				'company_id' => Configure::get("Blesta.company_id"),
				'level' => $tax->level,
				'name' => $this->decode($tax->name),
				'state' => $state ? $state->code : null,
				'country' => $tax->country != "" ? $tax->country : null,
				'amount' => $tax->taxrate
			);
			$this->local->insert("taxes", $vars);
			$tax_id = $this->local->lastInsertId();
			
			$this->mappings['taxes'][$tax->id] = $tax_id;
		}
		$this->local->commit();
		unset($taxes);
	}
	
	/**
	 * Import currencies
	 */
	protected function importCurrencies() {
		$this->loadModel("WhmcsCurrencies");
		
		$currencies = $this->fetchall ? $this->WhmcsCurrencies->get()->fetchAll() : $this->WhmcsCurrencies->get();
		foreach ($currencies as $currency) {
			$vars = array(
				'code' => $currency->code,
				'company_id' => Configure::get("Blesta.company_id"),
				'format' => $this->getCurrencyFormat((int)$currency->format),
				'prefix' => $this->decode($currency->prefix != "" ? $currency->prefix : null),
				'suffix' => $this->decode($currency->suffix != "" ? $currency->suffix : null),
				'exchange_rate' => $currency->rate,
				'exchange_updated' => null
			);
			$this->local->
				duplicate("format", "=", $vars['format'])->
				duplicate("prefix", "=", $vars['prefix'])->
				duplicate("suffix", "=", $vars['suffix'])->
				duplicate("exchange_rate", "=", $vars['exchange_rate'])->
				insert("currencies", $vars);
			
			// Set default currency
			if ($currency->default == "1") {
				$this->Companies->setSetting(Configure::get("Blesta.company_id"), "default_currency", $currency->code);
			}
		}
		unset($currencies);
	}

	/**
	 * Import invoices
	 */
	protected function importInvoices() {
		$this->loadModel("WhmcsConfiguration");
		$this->loadModel("WhmcsInvoices");
		Loader::loadModels($this, array("Invoices"));
		
		$cascade_tax = false;
		// Get compound tax setting
		$cascade = $this->WhmcsConfiguration->get("TaxL2Compound")->fetch();
		if ($cascade && $cascade->value == "on")
			$cascade_tax = true;
		
		$invoices = $this->fetchall ? $this->WhmcsInvoices->get()->fetchAll() : $this->WhmcsInvoices->get();
		$this->local->begin();
		foreach ($invoices as $invoice) {
			// Get tax rules
			$level1 = $this->getTaxRule(1, $invoice->taxrate);
			$level2 = $this->getTaxRule(2, $invoice->taxrate2);
			
			$status = "active";
			switch (strtolower($invoice->status)) {
				case "refunded":
				case "cancelled":
					$status = "void";
					break;
				default:
					$status = "active";
					break;
			}
			if (!$invoice->currency) {
                $invoice->currency = "AUD";
            }
			$vars = array(
				'id_format' => $this->decode($invoice->invoicenum != "" ? $invoice->invoicenum : "{num}"),
				'id_value' => $invoice->invoicenum != "" ? 0 : $invoice->id,
				'client_id' => $this->mappings['clients'][$invoice->userid],
				'date_billed' => $this->Companies->dateToUtc($invoice->date),
				'date_due' => $this->Companies->dateToUtc($invoice->duedate),
				'date_closed' => strtolower($invoice->status) != "paid" || $invoice->datepaid == "0000-00-00 00:00:00" ? null : $this->Companies->dateToUtc($invoice->datepaid),
				'date_autodebit' => null,
				'status' => $status,
				'previous_due' => 0,
				'currency' => $invoice->currency,
				'note_public' => $invoice->notes,
				'note_private' => null,
			);

			// Manually add the invoice so we can set the correct tax IDs and invoice ID
			$this->local->insert("invoices", $vars);
			$local_invoice_id = $this->local->lastInsertId();
			
			$this->mappings['invoices'][$invoice->id] = $local_invoice_id;
			$this->mappings['invoice_tax_rules'][$invoice->id] = array(
				'level1' => $level1,
				'level2' => $level2
			);
			if (!$invoice->currency) {
                $invoice->currency = "AUD";
            }
			if ($invoice->credit > 0) {
				$this->credits[] = array(
					'invoice_id' => $local_invoice_id,
					'client_id' => $this->mappings['clients'][$invoice->userid],
					'amount' => $invoice->credit,
					'currency' => $invoice->currency,			
					'transaction_id' => "invoice credit",
					'transaction_type' => "other",
					'transaction_type_id' => $this->getTransactionTypeId("in_house_credit"),
					'status' => 'approved',
					'date_added' => $this->Companies->dateToUtc($invoice->date, "c")
				);
			}
		}
		$this->local->commit();
		unset($invoices);
		
		// Import line items
		$lines = $this->fetchall ? $this->WhmcsInvoices->getLines()->fetchAll() : $this->WhmcsInvoices->getLines();
		$this->local->begin();
		foreach ($lines as $line) {
			if (!isset($this->mappings['invoices'][$line->invoiceid]))
				continue;
			
			// Import lines
			$vars = array(
				'invoice_id' => $this->mappings['invoices'][$line->invoiceid],
				'service_id' => null,
				'description' => $this->decode($line->description),
				'qty' => 1,
				'amount' => $line->amount,
				'order' => 0
			);
			$this->local->insert("invoice_lines", $vars);
			$line_id = $this->local->lastInsertId();
			
			// Import tax lines
			if ($line->taxed > 0) {
				if ($this->mappings['invoice_tax_rules'][$line->invoiceid]['level1']) {
					$vars = array(
						'line_id' => $line_id,
						'tax_id' => $this->mappings['invoice_tax_rules'][$line->invoiceid]['level1']->id
					);
					$this->local->insert("invoice_line_taxes", $vars);
				}
				
				if ($this->mappings['invoice_tax_rules'][$line->invoiceid]['level2']) {
					$vars = array(
						'line_id' => $line_id,
						'tax_id' => $this->mappings['invoice_tax_rules'][$line->invoiceid]['level2']->id,
						'cascade' => $cascade_tax ? 1 : 0
					);
					$this->local->insert("invoice_line_taxes", $vars);
				}
			}
		}
		$this->local->commit();
		unset($lines);
		
		// Update totals
		if (isset($this->mappings['invoices'])) {
			foreach ($this->mappings['invoices'] as $remote_invoice_id => $local_invoice_id) {
				$subtotal = $this->Invoices->getSubtotal($local_invoice_id);
				$total = $this->Invoices->getTotal($local_invoice_id);
				
				$this->local->where("id", "=", $local_invoice_id)->
					update("invoices", array('subtotal' => $subtotal, 'total' => $total));
			}
		}
		
		$periods = array(
			'Days' => "day",
			'Weeks' => "week",
			'Months' => "month",
			'Years' => "year"
		);
		
		// Import recurring invoices
		$lines = $this->fetchall ? $this->WhmcsInvoices->getRecurringLines()->fetchAll() : $this->WhmcsInvoices->getRecurringLines();
		$this->local->begin();
		foreach ($lines as $line) {
            if (!$line->currency) {
                $line->currency = "AUD";
            }
			if (!isset($periods[$line->recurcycle]))
				continue;
        
			$vars = array(
				'client_id' => $this->mappings['clients'][$line->userid],
				'term' => $line->recur,
				'period' => $periods[$line->recurcycle],
				'duration' => $line->recurfor > 0 ? $line->recurfor : null,
				'date_renews' => $this->Companies->dateToUtc($line->duedate),
				'currency' => $line->currency,
				'lines' => array(
					array(
						'description' => $this->decode($line->description),
						'qty' => 1,
						'amount' => $line->amount,
						'tax' => 0
					)
				),
				'delivery' => array('email')
			);
			
			$recurring_id = $this->Invoices->addRecurring($vars);
			if ($recurring_id)
				$this->mappings['recurring_invoices'][$line->id] = $recurring_id;
		}
		$this->local->commit();
		unset($lines);
		
        if (isset($this->mappings['recurring_invoices'])) {
			// Record each recurring invoice instance
			$this->local->begin();
			foreach ($this->mappings['recurring_invoices'] as $remote_id => $recurring_id) {
				$lines = $this->fetchall ? $this->WhmcsInvoices->getRecurInstances($remote_id)->fetchAll() : $this->WhmcsInvoices->getRecurInstances($remote_id);
				foreach ($lines as $line) {
					$vars = array(
						'invoice_recur_id' => $recurring_id,
						'invoice_id' => $this->mappings['invoices'][$line->invoiceid]
					);
					$this->local->insert("invoices_recur_created", $vars);
				}
				unset($lines);
			}
			$this->local->commit();
		}
	}
	
	/**
	 * Import transactions
	 */
	protected function importTransactions() {
		$this->loadModel("WhmcsAccounts");
		$this->loadModel("WhmcsCurrencies");
		Loader::loadModels($this, array("Invoices"));
		
		$default_currency = $this->WhmcsCurrencies->getDefaultCode();
        if (!$default_currency) {
                $default_currency = "AUD";
        }
		$invoice_ids = array();
		
		// Add invoice credits
		$this->local->begin();
		foreach ($this->credits as $credit) {
			$transaction_id = $this->addTransaction($credit, null);
			$vars = array(
				'date' => $credit['date_added'],
				'amounts' => array(
					array(
						'invoice_id' => $credit['invoice_id'],
						'amount' => $credit['amount'],
					)
				)
			);
			$this->Transactions->apply($transaction_id, $vars);
			
			if (!in_array($credit['invoice_id'], $invoice_ids))
				$invoice_ids[] = $credit['invoice_id'];
		}
		$this->local->commit();
		unset($this->credits);
		
		$transactions = $this->fetchall ? $this->WhmcsAccounts->get(true)->fetchAll() : $this->WhmcsAccounts->get(true);
		$this->local->begin();
		foreach ($transactions as $transaction) {
			if (!isset($this->mappings['clients'][$transaction->userid]))
				continue;
			
			$currency = $default_currency;
            if (!$currency) {
                $currency = "AUD";
            }
			if ($transaction->trans_currency != "")
				$currency = $transaction->trans_currency;
			elseif ($transaction->client_currency != "")
				$currency = $transaction->client_currency;
			
			// Only add income transactions
			if ($transaction->amountin > 0) {
				$status = ($transaction->refund > 0 ? "refunded" : "approved");
				$vars = array(
					'client_id' => $this->mappings['clients'][$transaction->userid],
					'amount' => $transaction->amountin,
					'currency' => $currency,
					'transaction_id' => $transaction->transid,
					'status' => $status,
					'date_added' => $this->Companies->dateToUtc($transaction->date, "c")
				);
				$transaction_id = $this->addTransaction($vars, $transaction->id);
				
				// If the transactions was refunded add a new transaction for the difference
				if ($status == "refunded" && $transaction->refund < $transaction->amountin) {
					$vars = array(
						'client_id' => $this->mappings['clients'][$transaction->userid],
						'amount' => $transaction->amountin - $transaction->refund,
						'currency' => $currency,
						'transaction_id' => $transaction->transid,
						'status' => "approved",
						'date_added' => $this->Companies->dateToUtc($transaction->date, "c")
					);
					$transaction_id = $this->addTransaction($vars, $transaction->id);
				}
			}
			
			// Apply payment
			if (isset($this->mappings['invoices'][$transaction->invoiceid]) && $transaction->amountin > 0) {
				$vars = array(
					'date' => $this->Companies->dateToUtc($transaction->date, "c"),
					'amounts' => array(
						array(
							'invoice_id' => $this->mappings['invoices'][$transaction->invoiceid],
							'amount' => $transaction->amountin - ($transaction->refund > 0 ? $transaction->refund : 0),
						)
					)
				);
				$this->Transactions->apply($transaction_id, $vars);
				
				if (!in_array($this->mappings['invoices'][$transaction->invoiceid], $invoice_ids))
					$invoice_ids[] = $this->mappings['invoices'][$transaction->invoiceid];
			}
		}
		$this->local->commit();
		unset($transactions);
		
		// Add client credits
		$credits = $this->fetchall ? $this->WhmcsAccounts->getOpenCredits()->fetchAll() : $this->WhmcsAccounts->getOpenCredits();
		$this->local->begin();
		foreach ($credits as $credit) {
            if (!$credit->currency) {
                $credit->currency = "AUD";
            }
			if (!isset($this->mappings['clients'][$credit->userid]))
				continue;
			
			$vars = array(
				'client_id' => $this->mappings['clients'][$credit->userid],
				'amount' => $credit->credit,
				'currency' => $credit->currency,
				'type' => 'other',
				'transaction_type_id' => $this->getTransactionTypeId("in_house_credit"),
				'transaction_id' => null,
				'status' => 'approved',
				'date_added' => $this->Companies->dateToUtc(date("c"))
			);
			$transaction_id = $this->addTransaction($vars, $transaction->id);
		}
		$this->local->commit();
		unset($credits);
		
		// Update paid totals
		$this->local->begin();
		foreach ($invoice_ids as $invoice_id) {
			// Update paid total
			$paid = $this->Invoices->getPaid($invoice_id);
			$this->local->where("id", "=", $invoice_id)->
				update("invoices", array('paid' => $paid));
		}
		$this->local->commit();
		
		$this->balanceClientCredit();
	}
	
	/**
	 * Verifies that total transaction credit for a each client matches credit
	 * set in WHMCS
	 */
	protected function balanceClientCredit() {
		if ($this->settings['balance_credit'] != "true")
			return;
		
		$this->loadModel("WhmcsAccounts");
		if (!isset($this->Transactions))
			Loader::loadModels($this, array("Transactions"));
		if (!isset($this->Invoices))
			Loader::loadModels($this, array("Invoices"));

		// Fetch all client credit values
		$credits = $this->WhmcsAccounts->getCredits();
		$date = date("c");
		foreach ($credits as $credit) {
			if (!isset($this->mappings['clients'][$credit->userid]))
				continue;
			
			$client_id = $this->mappings['clients'][$credit->userid];
			$total_credit = $this->Transactions->getTotalCredit($client_id, $credit->currency);
			$credit_diff = round($total_credit-$credit->credit, 4);
			
			// We have excess credit, so consume it
			if ($credit_diff > 0) {
				// Create an invoice to balance credits
				$vars = array(
					'client_id' => $client_id,
					'currency' => $credit->currency,
					'date_billed' => $date,
					'date_due' => $date,
					'status' => "active",
					'lines' => array(
						array(
							'description' => "Automatic credit balance adjustment.",
							'qty' => 1,
							'amount' => $credit_diff
						)
					)
				);
				$invoice_id = $this->Invoices->add($vars);
				
				// Consume the credit
				$amounts = array(
					array(
						'invoice_id' => $invoice_id,
						'amount' => $credit_diff
					)
				);
				$this->Transactions->applyFromCredits($client_id, $credit->currency, $amounts);
			}
			elseif ($credit_diff < 0) {
				// Create transaction to hold the credit diff
				$vars = array(
					'client_id' => $client_id,
					'amount' => -1*$credit_diff,
					'currency' => $credit->currency,
					'type' => 'other',
					'transaction_type_id' => $this->getTransactionTypeId("in_house_credit"),
					'transaction_id' => null,
					'status' => 'approved',
					'date_added' => $this->Companies->dateToUtc($date)
				);
				$transaction_id = $this->addTransaction($vars, $transaction->id);
			}
		}
	}
	
	/**
	 * Import modules
	 */
	protected function importModules() {
		$this->loadModel("WhmcsProducts");
		
		// Import generic server module required for all package assigned to no module
		$this->installModuleRow(array('id' => "generic_server", 'type' => "generic_server"));
		
		// Import servers
		$rows = $this->fetchall ? $this->WhmcsProducts->getServers()->fetchAll() : $this->WhmcsProducts->getServers();
		foreach ($rows as $row) {
			$this->installModuleRow((array)$row);
		}
		unset($rows);
		
		// Import registrars
		foreach ($this->WhmcsProducts->getReigstrars() as $registrar) {
			$row = $this->WhmcsProducts->getRegistrarFields($registrar);
			foreach ($row as &$value) {
				$value = $this->decryptData($value);
			}
			
			$row['id'] = $registrar;
			$row['type'] = $registrar;
			
			$this->installModuleRow($row, "registrar");
		}
	}
	
	/**
	 * Import packages
	 */
	protected function importPackages() {
		$this->importModules();
		
		$this->loadModel("WhmcsProducts");
		$this->loadModel("WhmcsConfiguration");
		Loader::loadModels($this, array("PackageGroups"));
		
		// Add imported package group
		$vars = array(
			'company_id' => Configure::get("Blesta.company_id"),
			'name' => "Imported",
			'type' => "standard"
		);
		$package_group_id = $this->PackageGroups->add($vars);
		
		$products = $this->WhmcsProducts->get()->fetchAll();
		$i=1;
		$this->local->begin();
		foreach ($products as $product) {

			if (!isset($this->mappings['modules'][$product->servertype]))
				$product->servertype = "generic_server";
			
			$pricing = $this->WhmcsProducts->getPricing($product->id);
			$mapping = $this->getModuleMapping($product->servertype);
			
			// Add package
			$vars = array(
				'id_format' => "{num}",
				'id_value' => $product->id,
				'module_id' => $this->mappings['modules'][$product->servertype],
				'name' => $this->decode($product->name),
				'description' => strip_tags($this->decode($product->description)),
				'description_html' => $this->decode($product->description),
				'qty' => $product->stockcontrol == "on" ? $product->qty : null,
				'module_row' => 0, // WHMCS doesn't associate a service with a product
				'module_group' => null,
				'taxable' => $product->tax,
				'status' => $product->retired == "1" ? "inactive" : "active",
				'company_id' => Configure::get("Blesta.company_id")
			);
			$this->local->insert("packages", $vars);
			$this->mappings['packages'][$product->id] = $this->local->lastInsertId();
			
			// Assign group
			$this->local->insert("package_group", array('package_id' => $this->mappings['packages'][$product->id], 'package_group_id' => $package_group_id));
			
			// Add package pricing
			$this->addPackagePricing($pricing, $this->mappings['packages'][$product->id]);
			
			// Import package meta
			$this->addPackageMeta((array)$product, $mapping);
			$i = max(++$i, $product->id);
		}
		$this->local->commit();
		
		
		$taxable = 0;
		$tax_domains = $this->WhmcsConfiguration->get("TaxDomains")->fetch();
		if ($tax_domains)
			$taxable = $tax_domains->value == "on" ? 1 : 0;

		$tlds = $this->WhmcsProducts->getTlds();
		$this->local->begin();
		foreach ($tlds as $tld) {
			
			$pricing = $this->WhmcsProducts->getTldPricing($tld->extension);
			$registrar = trim($tld->autoreg);
			if ($registrar == "")
				continue;
			
			$mapping = $this->getModuleMapping($registrar, "registrar");
			
			$vars = array(
				'id_format' => "{num}",
				'id_value' => max($tld->id, $i++),
				'module_id' => $this->mappings['modules'][$registrar],
				'name' => "Domain Registration (" . $tld->extension . ")",
				'description' => null,
				'description_html' => null,
				'qty' => null,
				'module_row' => !isset($this->mappings['module_rows'][$registrar][$registrar]) ? 0 : $this->mappings['module_rows'][$registrar][$registrar],
				'module_group' => null,
				'taxable' => $taxable,
				'status' => "active",
				'company_id' => Configure::get("Blesta.company_id")
			);

			// Add the package
			$this->local->insert("packages", $vars);
			$this->mappings['packages'][$tld->extension . $registrar] = $this->local->lastInsertId();

			// Assign group
			$this->local->insert("package_group", array('package_id' => $this->mappings['packages'][$tld->extension . $registrar], 'package_group_id' => $package_group_id));

			// Add package pricing
			$this->addPackagePricing($pricing, $this->mappings['packages'][$tld->extension . $registrar]);
			
			// Import package meta
			$product = array(
				'id' => $tld->extension . $registrar,
				'tlds' => array($tld->extension)
			);
			$this->addPackageMeta($product, $mapping);
		}
		$this->local->commit();
	}
	
	/**
	 * Import package options
	 */
	protected function importPackageOptions() {
		$this->loadModel("WhmcsProducts");
		Loader::loadModels($this, array("PackageOptionGroups", "PackageOptions"));
		
		$option_types = $this->WhmcsProducts->getConfigOptionTypes();
		
		$option_groups = $this->WhmcsProducts->getConfigOptionGroups();
		
		foreach ($option_groups as $option_group) {
			$packages = array();
			// Map WHMCS packages to packages in Blesta
			foreach ($option_group->packages as $package_id) {
				if (isset($this->mappings['packages'][$package_id])) {
					$packages[] = $this->mappings['packages'][$package_id];
				}
			}
			
			$vars = array(
				'company_id' => Configure::get("Blesta.company_id"),
				'name' => $option_group->name,
				'description' => $option_group->description,
				'packages' => $packages
			);
			$option_group_id = $this->PackageOptionGroups->add($vars);
			
			// Record package group mapping
			$this->mappings['package_options_groups'][$option_group->id] = $option_group_id;
			
			// Import package options
			$options = $this->WhmcsProducts->getConfigOptions($option_group->id);
			foreach ($options as $option) {

				$values = array();				
				foreach ($option->values as $value) {
					$is_qty = isset($option_types[$option->optiontype]) && $option_types[$option->optiontype] == "quantity";
					
					$values[] = array(
						'name' => $value->optionname,
						'value' => $is_qty ? null : $value->optionname,
						'min' => $is_qty ? max(0, $option->qtyminimum) : null,
						'max' => $is_qty && $option->qtymaximum > 0 ? max(1, $option->qtymaximum) : null,
						'step' => $is_qty ? "1" : null,
						'pricing' => $this->WhmcsProducts->getPricing($value->id, "configoptions")
					);
				}
				
				// WHMCS only supports one group per option... weak!
				$groups = array($this->mappings['package_options_groups'][$option->gid]);
				
				$vars = array(
					'company_id' => Configure::get("Blesta.company_id"),
					'label' => $option->optionname,
					'name' => $option->optionname,
					'type' => isset($option_types[$option->optiontype]) ? $option_types[$option->optiontype] : "select",
					'values' => $values,
					'groups' => $groups
				);
				$option_id = $this->PackageOptions->add($vars);
				
				// Record package option mapping
				$this->mappings['package_options'][$option->id] = $option_id;
				
				// Record package option value mappings
				$opt_values = $this->PackageOptions->getValues($option_id);
				foreach ($opt_values as $v => $val) {
					$this->mappings['option_values'][$option->values[$v]->id] = $val->id;
				}
			}
		}
	}
	
	/**
	 * Import services
	 */
	protected function importServices() {
		$this->loadModel("WhmcsServices");
		$this->loadModel("WhmcsProducts");
		Loader::loadModels($this, array("Clients", "Packages"));
		
		$servers = array();
		$rows = $this->fetchall ? $this->WhmcsProducts->getServers()->fetchAll() : $this->WhmcsProducts->getServers();
		foreach ($rows as $row) {
			$servers[$row->id] = $row;
		}
		unset($rows);
		
		$services = $this->fetchall ? $this->WhmcsServices->get()->fetchAll() : $this->WhmcsServices->get();
		$this->local->begin();
		foreach ($services as $service) {
		
			// If the client doesn't exist, we can't import the service
			if (!isset($this->mappings['clients'][$service->userid]))
				continue;
			// If the package doesn't exist, we can't import the service
			if (!isset($this->mappings['packages'][$service->packageid]))
				continue;

			$package = $this->Packages->get($this->mappings['packages'][$service->packageid]);
			
			if (!isset($this->mappings['modules'])) {
				if (!isset($this->ModuleManager))
					Loader::loadModels($this, array("ModuleManager"));
					
				$module = $this->ModuleManager->get($package->module_id, false, false);
				if ($module)
					$modules[$package->module_id] = $module->class;
			}
			else
				$modules = array_flip($this->mappings['modules']);
				
			$mapping = $this->getModuleMapping(isset($modules[$package->module_id]) ? $modules[$package->module_id] : "generic_server");
			
			// Get currency this client is invoiced in
			$currency = $this->getCurrency($this->mappings['clients'][$service->userid]);
            if (!$currency) {
                $currency = "AUD";
            }
			
			if ($package->module_row > 0)
				$module_row_id = $package->module_row;
			else {
				if (isset($mapping['module_row_key']) && isset($servers[$service->server]->{$mapping['module_row_key']}))
					$module_row_id = $this->getModuleRowId($package->module_id, $servers[$service->server]->{$mapping['module_row_key']}, null);
				else
					$module_row_id = $this->getModuleRowId($package->module_id, null, isset($modules[$package->module_id]) ? $modules[$package->module_id] : null);
			}
			if (!$module_row_id)
				continue;
			
			$status = $this->getServiceStatus($service->domainstatus);
			$pricing = $this->getPricing($this->WhmcsServices->getTerm($service->billingcycle), $package, $currency, $service->amount);
			$override_price = (($p = number_format($pricing->price, 2, '.', '')) == number_format($service->amount, 2, '.', '') ? $p : null);
			$override_currency = ($override_price === null ? null : $currency);
			
			$vars = array(
				'parent_service_id' => null,
				'package_group_id' => null,
				'id_format' => "{num}",
				'id_value' => $service->id,
				'pricing_id' => $pricing->id,
				'client_id' => $this->mappings['clients'][$service->userid],
				'module_row_id' => $module_row_id,
				'coupon_id' => null,
				'qty' => 1,
				'override_price' => $override_price,
				'override_currency' => $override_currency,
				'status' => $status,
				'date_added' => $this->Companies->dateToUtc($service->regdate . " 00:00:00"),
				'date_renews' => $service->nextinvoicedate == "0000-00-00" ? null : $this->Companies->dateToUtc($service->nextinvoicedate . " 00:00:00"),
				'date_last_renewed' => null,
				'date_suspended' => $status == "suspended" ? $this->Companies->dateToUtc(date("c")) : null,
				'date_canceled' => $status == "canceled" ? $this->Companies->dateToUtc(date("c")) : null
			);
			
			$this->local->insert("services", $vars);
			$service_id = $this->local->lastInsertId();
			$this->mappings['services'][$service->id] = $service_id;
			
			$this->addServiceFields((array)$service, $mapping);
		}
		$this->local->commit();
		unset($services);
		
		$option_types = $this->WhmcsProducts->getConfigOptionTypes();
		
		// Import options for services
		$options = $this->WhmcsServices->getConfigOptions();
		$this->local->begin();
		foreach ($options as $option) {
			// Ensure parent service exists
			if (!isset($this->mappings['services'][$option->relid]))
				continue;
			
			$currency = $this->getCurrency($this->mappings['clients'][$option->userid]);
            if (!$currency) {
                $currency = "AUD";
            }
			$value_id = $this->mappings['option_values'][$option->optionid];
			$pricing = $this->getOptionPricing($this->WhmcsServices->getTerm($option->billingcycle), $value_id, $currency);
            if (!$pricing) {
                continue;
            }
			
			$vars = array(
				'service_id' => $this->mappings['services'][$option->relid],
				'option_pricing_id' => $pricing->id,
				// option isn't a quantity type, set qty to 1
				'qty' => $option_types[$option->optiontype] == "quantity" ? $option->qty : 1
			);
			$this->local->insert("service_options", $vars);
		}
		$this->local->commit();
		unset($options);
		
		$services = $this->fetchall ? $this->WhmcsServices->getDomains()->fetchAll() : $this->WhmcsServices->getDomains();
		$this->local->begin();
		foreach ($services as $service) {
		
			// If the client doesn't exist, we can't import the service
			if (!isset($this->mappings['clients'][$service->userid]))
				continue;
			
			if ($service->registrar == "")
				$service->registrar = "generic_registrar";
			
			$tld = $this->getTld($service->domain, $service->registrar);
			
			// If package does not exist, we can't import the service
			if (!isset($this->mappings['packages'][$tld . $service->registrar]))
				continue;
			
			$package = $this->Packages->get($this->mappings['packages'][$tld . $service->registrar]);
			$mapping = $this->getModuleMapping($service->registrar, "registrar");
			
			// Get currency this client is invoiced in
			$currency = $this->getCurrency($this->mappings['clients'][$service->userid]);
            if (!$currency) {
                $currency = "AUD";
            }
			
			$module_row_id = $this->mappings['module_rows'][$service->registrar][$service->registrar];
			
			if (!$module_row_id)
				continue;
			
			$status = $this->getServiceStatus($service->status);
			$pricing = $this->getPricing($this->WhmcsServices->getTerm($service->registrationperiod), $package, $currency, $service->recurringamount);
			$override_price = (($p = number_format($pricing->price, 2, '.', '')) == number_format($service->recurringamount, 2, '.', '') ? $p : null);
			$override_currency = ($override_price === null ? null : $currency);
			
			$vars = array(
				'parent_service_id' => null,
				'package_group_id' => null,
				'id_format' => "{num}",
				'id_value' => $service->id,
				'pricing_id' => $pricing->id,
				'client_id' => $this->mappings['clients'][$service->userid],
				'module_row_id' => $module_row_id,
				'coupon_id' => null,
				'qty' => 1,
				'override_price' => $override_price,
				'override_currency' => $override_currency,
				'status' => $status,
				'date_added' => $this->Companies->dateToUtc($service->registrationdate . " 00:00:00"),
				'date_renews' => $service->nextinvoicedate == "0000-00-00" ? null : $this->Companies->dateToUtc($service->nextinvoicedate . " 00:00:00"),
				'date_last_renewed' => null,
				'date_suspended' => $status == "suspended" ? $this->Companies->dateToUtc(date("c")) : null,
				'date_canceled' => $status == "canceled" ? $this->Companies->dateToUtc(date("c")) : null
			);
			
			$this->local->insert("services", $vars);
			$service_id = $this->local->lastInsertId();
			$this->mappings['services'][$service->id] = $service_id;
			
			$this->addServiceFields((array)$service, $mapping);
		}
		$this->local->commit();
		unset($services);
	}
	
	/**
	 * Import support departments
	 */
	protected function importSupportDepartments() {
		Loader::loadModels($this, array("PluginManager"));

		// Install support plugin if not already installed
		if (!$this->PluginManager->isInstalled("support_manager", Configure::get("Blesta.company_id")))
			$this->PluginManager->add(array('dir' => "support_manager", 'company_id' => Configure::get("Blesta.company_id")));
		
		$this->loadModel("WhmcsSupportDepartments");
		
		$departments = $this->fetchall ? $this->WhmcsSupportDepartments->get()->fetchAll() : $this->WhmcsSupportDepartments->get();
		$this->local->begin();
		foreach ($departments as $department) {
			$vars = array(
				'company_id' => Configure::get("Blesta.company_id"),
				'name' => $this->decode($department->name),
				'description' => $this->decode($department->description),
				'email' => $this->decode($department->email),
				'method' => $department->piperepliesonly == "on" ? "pipe" : "pop3",
				'default_priority' => "medium",
				'host' => $this->decode($department->host),
				'user' => $this->decode($department->login),
				'pass' => $this->decryptData($department->password),
				'port' => $department->port,
				'security' => "none",
				'box_name' => null,
				'mark_messages' => "deleted",
				'clients_only' => $department->clientsonly == "on" ? 1 : 0,
				'status' => $department->hidden == "on" ? "hidden" : "visible"
			);
			$this->local->insert("support_departments", $vars);
			$department_id = $this->local->lastInsertId();
			$this->mappings['support_departments'][$department->id] = $department_id;
		}
		$this->local->commit();
		unset($departments);
		
		// Assign admins to support departments
		$this->local->begin();
		foreach ($this->mappings['admin_departs'] as $remote_admin_id => $departs) {
			if (!isset($this->mappings['staff'][$remote_admin_id]))
				continue;
			
			$departs = explode(",", $departs);
			foreach ($departs as $depart_id) {
				$depart_id = trim($depart_id);
				if (isset($this->mappings['support_departments'][$depart_id])) {
					
					$vars = array(
						'department_id' => $this->mappings['support_departments'][$depart_id],
						'staff_id' => $this->mappings['staff'][$remote_admin_id]
					);
					$this->local->
						duplicate("staff_id", "=", $this->mappings['staff'][$remote_admin_id])->
						insert("support_staff_departments", $vars);
				}
			}
			
			// Add schedules
			$days = array("sun", "mon", "tue", "wed", "thu", "fri", "sat");
			foreach ($days as $day) {
				$vars = array(
					'staff_id' => $this->mappings['staff'][$remote_admin_id],
					'company_id' => Configure::get("Blesta.company_id"),
					'day' => $day,
					'start_time' => "00:00:00",
					'end_time' => "00:00:00"
				);
				try {
					$this->local->insert("support_staff_schedules", $vars);
				}
				catch (Exception $e) {
					$this->local->reset();
				}
			}
			
			// Add notices
			$keys = array("ticket_emails");
			foreach ($keys as $key) {
				$vars = array(
					'key' => $key,
					'company_id' => Configure::get("Blesta.company_id"),
					'staff_id' => $this->mappings['staff'][$remote_admin_id],
					'value' => serialize(array('emergency' => "true", 'critical' => "true", 'high' => "true", 'medium' => "true", 'low' => "true"))
				);
				try {
					$this->local->insert("support_staff_settings", $vars);
				}
				catch (Exception $e) {
					$this->local->reset();
				}
			}
		}
		$this->local->commit();
	}
	
	/**
	 * Import support tickets
	 */
	protected function importSupportTickets() {
		$this->loadModel("WhmcsSupportTickets");
		
		$priorities = array(
			'High' => 'high',
			'Medium' => 'medium',
			'Low' => 'low'
		);
		$statuses = array(
			'Open' => 'open',
			'Answered' => 'closed',
			'Customer-Reply' => 'awaiting_reply',
			'Closed' => 'closed',
			'In Progress' => 'in_progress'
		);
		
		$tickets = $this->fetchall ? $this->WhmcsSupportTickets->get()->fetchAll() : $this->WhmcsSupportTickets->get();
		$this->local->begin();
		foreach ($tickets as $ticket) {
			$vars = array(
				'code' => is_numeric($ticket->tid) ? (int)$ticket->tid : preg_replace("/[^0-9]+/", "", $ticket->tid),
				'department_id' => isset($this->mappings['support_departments'][$ticket->did]) ? $this->mappings['support_departments'][$ticket->did] : 0,
				'staff_id' => null,
				'service_id' => null,
				'client_id' => $ticket->userid > 0 && isset($this->mappings['clients'][$ticket->userid]) ? $this->mappings['clients'][$ticket->userid] : null,
				'email' => $this->decode($ticket->email != "" ? $ticket->email : null),
				'summary' => $this->decode($ticket->title),
				'priority' => isset($priorities[$ticket->urgency]) ? $priorities[$ticket->urgency] : 'medium',
				'status' => isset($statuses[$ticket->status]) ? $statuses[$ticket->status] : 'open',
				'date_added' => $this->Companies->dateToUtc($ticket->date),
				'date_closed' => isset($statuses[$ticket->status]) && $statuses[$ticket->status] == "closed" ? $this->Companies->dateToUtc($ticket->lastreply) : null,
			);

			$this->local->insert("support_tickets", $vars);
			$this->mappings['support_tickets'][$ticket->id] = $this->local->lastInsertId();
			
			// Add ticket body
			$vars = array(
				'ticket_id' => $this->mappings['support_tickets'][$ticket->id],
				'staff_id' => $ticket->admin_id && isset($this->mappings['staff'][$ticket->admin_id]) ? $this->mappings['staff'][$ticket->admin_id] : null,
				'type' => "reply",
				'details' => $this->decode($ticket->message),
				'date_added' => $this->Companies->dateToUtc($ticket->date),
				
			);
			$this->local->insert("support_replies", $vars);
		}
		$this->local->commit();
		unset($tickets);

		// Import ticket replies
		$replies = $this->fetchall ? $this->WhmcsSupportTickets->getReplies()->fetchAll() : $this->WhmcsSupportTickets->getReplies();
		$this->local->begin();
		foreach ($replies as $reply) {
			if (!isset($this->mappings['support_tickets'][$reply->tid]))
				continue;
			
			$vars = array(
				'ticket_id' => $this->mappings['support_tickets'][$reply->tid],
				'staff_id' => $reply->admin_id && isset($this->mappings['staff'][$reply->admin_id]) ? $this->mappings['staff'][$reply->admin_id] : null,
				'type' => "reply",
				'details' => $this->decode($reply->message),
				'date_added' => $this->Companies->dateToUtc($reply->date),
				
			);
			$this->local->insert("support_replies", $vars);
		}
		$this->local->commit();
		unset($replies);
		
		// Import ticket notes
		$notes = $this->fetchall ? $this->WhmcsSupportTickets->getNotes()->fetchAll() : $this->WhmcsSupportTickets->getNotes();
		$this->local->begin();
		foreach ($notes as $note) {
			if (!isset($this->mappings['support_tickets'][$note->ticketid]))
				continue;
			
			$vars = array(
				'ticket_id' => $this->mappings['support_tickets'][$note->ticketid],
				'staff_id' => $note->admin_id && isset($this->mappings['staff'][$note->admin_id]) ? $this->mappings['staff'][$note->admin_id] : null,
				'type' => "note",
				'details' => $this->decode($note->message),
				'date_added' => $this->Companies->dateToUtc($note->date),
				
			);
			$this->local->insert("support_replies", $vars);
		}
		$this->local->commit();
		unset($notes);
		
		// Import predefined categories
		$categories = $this->fetchall ? $this->WhmcsSupportTickets->getResponseCategories()->fetchAll() : $this->WhmcsSupportTickets->getResponseCategories();
		$this->local->begin();
		foreach ($categories as $category) {
			$vars = array(
				'company_id' => Configure::get("Blesta.company_id"),
				'parent_id' => isset($this->mappings['support_response_categories'][$category->parentid]) ? $this->mappings['support_response_categories'][$category->parentid] : null,
				'name' => $this->decode($category->name)
			);
			$this->local->insert("support_response_categories", $vars);
			$this->mappings['support_response_categories'][$category->id] = $this->local->lastInsertId();
		}
		$this->local->commit();
		unset($categories);
		
		// Import predefined replies
		$responses = $this->fetchall ? $this->WhmcsSupportTickets->getResponses()->fetchAll() : $this->WhmcsSupportTickets->getResponses();
		$this->local->begin();
		foreach ($responses as $response) {
			if (!isset($this->mappings['support_response_categories'][$response->catid]))
				continue;
			
			$vars = array(
				'category_id' => $this->mappings['support_response_categories'][$response->catid],
				'name' => $this->decode($response->name),
				'details' => $this->decode($response->reply)
			);
			$this->local->insert("support_responses", $vars);
		}
		$this->local->commit();
		unset($responses);
	}
	
	/**
	 * Import miscellaneous
	 */
	protected function importMisc() {
		$this->loadModel("WhmcsEmails");
		$this->loadModel("WhmcsConfiguration");
		$this->loadModel("WhmcsCalendar");
		
		$from_name = $this->WhmcsConfiguration->get("SystemEmailsFromName")->fetch();
		$from_email = $this->WhmcsConfiguration->get("SystemEmailsFromEmail")->fetch();
		
		// Mail log
		$this->startTimer("mail log");
		$this->local->begin();
		foreach ($this->WhmcsEmails->get() as $message) {
			if ($message->to == "")
				continue;
			$vars = array(
				'company_id' => Configure::get("Blesta.company_id"),
				'to_client_id' => $message->userid > 0 && isset($this->mappings['clients'][$message->userid]) ? $this->mappings['clients'][$message->userid] : null,
				'from_staff_id' => null,
				'to_address' => $this->decode($message->to),
				'from_address' => $this->decode($from_email->value),
				'from_name' => $this->decode($from_name->value),
				'cc_address' => $this->decode($message->cc != "" ? $message->cc : null),
				'subject' => $this->decode($message->subject != "" ? $message->subject : " "),
				'body_text' => strip_tags($this->decode($message->message)),
				'body_html' => $this->decode($message->message),
				'sent' => 1,
				'error' => null,
				'date_sent' => $this->Companies->dateToUtc($message->date)
			);
			$this->local->insert("log_emails", $vars);
		}
		$this->local->commit();
		$this->endTimer("mail log");
	
		// Configurations
		$this->startTimer("settings");
		$settings = $this->fetchall ? $this->WhmcsConfiguration->get()->fetchAll() : $this->WhmcsConfiguration->get();
		$this->local->begin();
		foreach ($settings as $setting) {
			$setting->value = $this->decode($setting->value);
			
			switch ($setting->setting) {
				case "MailType":
					$this->Companies->setSetting(Configure::get("Blesta.company_id"), "mail_delivery", $setting->value == "mail" ? "php" : $setting->value);
					break;
				case "SMTPHost":
					$this->Companies->setSetting(Configure::get("Blesta.company_id"), "smtp_host", $setting->value);
					break;
				case "SMTPUsername":
					$this->Companies->setSetting(Configure::get("Blesta.company_id"), "smtp_user", $setting->value);
					break;
				case "SMTPPassword":
					if ($setting->value != "")
						$this->Companies->setSetting(Configure::get("Blesta.company_id"), "smtp_user", $this->decryptData($setting->value));
					break;
				case "SMTPPort":
					$this->Companies->setSetting(Configure::get("Blesta.company_id"), "smtp_port", $setting->value);
					break;
				case "SMTPSSL":
					$this->Companies->setSetting(Configure::get("Blesta.company_id"), "smtp_security", $setting->value);
					break;
				case "CreateInvoiceDaysBefore":
					$this->Companies->setSetting(Configure::get("Blesta.company_id"), "inv_days_before_renewal", $setting->value);
					break;
				case "SendInvoiceReminderDays":
					$this->Companies->setSetting(Configure::get("Blesta.company_id"), "notice1", $setting->value != 0 ? -1 * $setting->value : "disabled");
					break;
				case "SendFirstOverdueInvoiceReminder":
					$this->Companies->setSetting(Configure::get("Blesta.company_id"), "notice2", $setting->value != 0 ? $setting->value : "disabled");
					break;
				case "SendSecondOverdueInvoiceReminder":
					$this->Companies->setSetting(Configure::get("Blesta.company_id"), "notice3", $setting->value != 0 ? $setting->value : "disabled");
					break;
				case "CCProcessDaysBefore":
					$this->Companies->setSetting(Configure::get("Blesta.company_id"), "autodebit_days_before_due", $setting->value);
					break;
				case "AutoSuspensionDays":
					$this->Companies->setSetting(Configure::get("Blesta.company_id"), "suspend_services_days_after_due", $setting->value);
					break;
			}
		}
		$this->local->commit();
		$this->endTimer("settings");
		unset($settings);
		
		// Import calendar events
		$this->startTimer("calendar events");
		$events = $this->fetchall ? $this->WhmcsCalendar->get()->fetchAll() : $this->WhmcsCalendar->get();
		$this->local->begin();
		foreach ($events as $event) {
			$vars = array(
				'company_id' => Configure::get("Blesta.company_id"),
				'staff_id' => isset($this->mappings['staff'][$event->adminid]) ? $this->mappings['staff'][$event->adminid] : 0,
				'shared' => 0,
				'title' => $this->decode($event->title . " " . $event->desc),
				'url' => null,
				'start_date' => $this->Companies->dateToUtc($event->start),
				'end_date' => $event->end != 0 ? $this->Companies->dateToUtc($event->end) : $this->Companies->dateToUtc($event->start),
				'all_day' => $event->allday
			);
			$this->local->insert("calendar_events", $vars);
		}
		$this->local->commit();
		$this->endTimer("calendar events");
		unset($events);
		
		// Import todo events
		$this->startTimer("todo events");
		$events = $this->fetchall ? $this->WhmcsCalendar->getTodos()->fetchAll() : $this->WhmcsCalendar->getTodos();
		$this->local->begin();
		foreach ($events as $event) {
			$vars = array(
				'company_id' => Configure::get("Blesta.company_id"),
				'staff_id' => isset($this->mappings['staff'][$event->admin]) ? $this->mappings['staff'][$event->admin] : 0,
				'shared' => 0,
				'title' => $this->decode($event->title . " " . $event->description),
				'url' => null,
				'start_date' => $this->Companies->dateToUtc($event->date),
				'end_date' => $this->Companies->dateToUtc($event->duedate),
				'all_day' => 1
			);
			$this->local->insert("calendar_events", $vars);
		}
		$this->local->commit();
		$this->startTimer("todo events");
		unset($events);
	}
	
	/**
	 * Load the given local model
	 *
	 * @param string $name The name of the model to load
	 */
	protected function loadModel($name) {
		$name = Loader::toCamelCase($name);
		$file = Loader::fromCamelCase($name);
		Loader::load($this->path . DS . "models" . DS . $file . ".php");
		$this->{$name} = new $name($this->remote);
	}
	
	/**
	 * Creates a user
	 *
	 * @param array $user An array of key/value pairs including:
	 * 	- username
	 * 	- password
	 * 	- date_added
	 */
	private function createUser(array $user) {
		$this->local->insert("users", $user);
		return $this->local->lastInsertId();
	}
	
	/**
	 * Returns the field type
	 *
	 * @param string WHMCS field type
	 * @return string Blesta field type
	 */
	private function getFieldType($type) {
		switch ($type) {
			case "text":
			case "textarea":
				return $type;
				break;
			case "dropdown":
				return "select";
			case "tickbox":
				return "checkbox";
			default:
				return "text";
		}
	}
	
	/**
	 * Returns the serialized selection of field values
	 *
	 * @param string WHMCS serialized values
	 * @return string Blesta serialized values
	 */
	private function getFieldValues($values) {
		if ($values == "")
			return null;
		$values = explode(",", $values);
		return serialize(array_combine($values, $values));
	}
	
	/**
	 * Returns the currency format
	 *
	 * @param int WHMCS currency format value
	 * @return string Blesta currency format value
	 */
	private function getCurrencyFormat($format) {
		switch ($format) {
			default:
			case 1:
			case 2:
				return "#,###.##";
			case 3:
				return "#.###,##";
			case 4:
				return "#,###";
		}
	}
	
	/**
	 * Returns the local tax rule for the given level and rate
	 *
	 * @param int $level
	 * @param float $rate
	 * @return mixed A stdClass object representing the local tax rule, false if no rule exists
	 */
	private function getTaxRule($level, $rate) {
		return $this->local->select()->from("taxes")->
			where("company_id", "=", Configure::get("Blesta.company_id"))->
			where("level", "=", $level)->where("amount", "=", $rate)->fetch();
	}
	
	/**
	 * Returns the transaction type ID
	 *
	 * @param string $type The version 2 transaction type
	 * @return string The transaction type ID
	 */	
	private function getTransactionTypeId($type) {
		static $trans_types = null;
		
		if (!isset($this->Transactions))
			Loader::loadModels($this, array("Transactions"));
		
		if ($trans_types == null)
			$trans_types = $this->Transactions->getTypes();
		
		switch ($type) {
			default:
			case "other":
			case "credit":
				return null;
			case "cash":
			case "check":
				foreach ($trans_types as $trans_type) {
					if ($trans_type->name == $type)
						return $trans_type->id;
				}
			case "in_house_credit":
				foreach ($trans_types as $trans_type) {
					if ($trans_type->name == "in_house_credit")
						return $trans_type->id;
				}
			case "money_order":
				foreach ($trans_types as $trans_type) {
					if ($trans_type->name == "money_order")
						return $trans_type->id;
				}
		}
		return null;
	}
	
	/**
	 * Convert WHMCS service status into Blesta service status
	 *
	 * @param string $status
	 * @return string The service status
	 */
	private function getServiceStatus($status) {
		$status = strtolower($status);
		switch ($status) {
			case "active":
			case "pending":
			case "suspended":
				return $status;
			case "fraud":
			case "terminated":
			case "cancelled":
				return "canceled";
		}
		return "in_review";
	}
	
	/**
	 * Decrypts data from WHMCS
	 *
	 * @param string $data The data to decrypt
	 * @return string The decrypted data
	 */
	private function decryptData($str) {
		$this->decrypt_count++;
		$this->unpauseTimer("decrypt");
		$key = $this->settings['key'];
		$y = base64_decode($str);
		$x = null;
		
		// Key derivation
		$key = sha1(md5(md5($key)) . md5($key));
		$temp_key = null;
		for ($i=0; $i<strlen($key); $i+=2) {
			$temp_key .= chr(hexdec($key[$i] . $key[$i + 1]));
		}
		$key = $temp_key;
		$key_length = strlen($key);
		
		// Extract key seed from input
		$key_seed = substr($y, 0, $key_length);
		$y = substr($y, $key_length, strlen($y) - $key_length);

		// Calculate final key
		$z = null;		
		for ($i=0; $i<$key_length; $i++) {
			$z .= chr(ord($key_seed[$i]) ^ ord($key[$i]));
		}
		
		// Decrypt
		for ($i=0; $i<strlen($y); $i++) {
			// Generate new key schedule for each block
			if ($i != 0 && $i % $key_length == 0) {
				$temp = sha1($z . substr($x, $i - $key_length, $key_length));
				$z = null;
				for ($j=0; $j<strlen($temp); $j+=2) {
					$z .= chr(hexdec($temp[$j] . $temp[$j+1]));
				}
			}
			
			$x .= chr(ord($z[$i % $key_length]) ^ ord($y[$i]));
		}
		$this->pauseTimer("decrypt");
		return $x;
	}
	
	/**
	 * Formats the given key into a mysql AES key
	 *
	 * @param string $key The AES key to format
	 * @return string The mysql formatted AES key
	 */
	private function mysqlAesKey($key) {
		$new_key = str_repeat(chr(0), 16);
		for($i=0,$len=strlen($key);$i<$len;$i++) {
			$new_key[$i%16] = $new_key[$i%16] ^ $key[$i];
		}
		return $new_key;
	}
	
	/**
	 * Installs the module row
	 *
	 * @param array $row An array of key/value pairs, including but not limited to:
	 * 	- type The module name in WHMCS
	 * 	- id The row ID in WHMCS
	 * @param string $module_type The type of module ('server' or 'registrar')
	 * @return int The module row ID installed (also saved in mappings['modules'] property)
	 */
	private function installModuleRow($row, $module_type = "server") {
		if (!isset($this->ModuleManager))
			Loader::loadModels($this, array("ModuleManager"));

		$mapping = $this->getModuleMapping($row['type'], $module_type);
		$module_id = $this->installModule($row['type'], $mapping);
		
		if (!$module_id)
			return null;
		
		// Install the module row
		$this->local->insert("module_rows", array('module_id' => $module_id));
		$module_row_id = $this->local->lastInsertId();
		
		$this->mappings['module_rows'][$row['type']][$row['id']] = $module_row_id;
		
		// Install the module row meta fields
		if (isset($mapping['module_row_meta'])) {
			foreach ($mapping['module_row_meta'] as $meta_row) {
				$vars = (array)$meta_row;
				$vars['value'] = null;
				$vars['module_row_id'] = $module_row_id;
				if (is_object($meta_row->value)) {
					$row_key = strtolower($meta_row->value->module);

					if (isset($meta_row->value->module) && array_key_exists($row_key, $row))
						$vars['value'] = $row[$row_key];
				}
				else
					$vars['value'] = $meta_row->value;
				
				if (isset($meta_row->callback))
					$vars['value'] = call_user_func_array($meta_row->callback, array($vars['value']));
				
				if ($vars['serialized'] == 1)
					$vars['value'] = serialize($vars['value']);
				if ($vars['encrypted'] == 1)
					$vars['value'] = $this->ModuleManager->systemEncrypt($vars['value']);
				
				$this->local->insert("module_row_meta", $vars, array("module_row_id", "key", "value", "serialized", "encrypted"));
			}
		}
		
		return $module_row_id;
	}
	
	/**
	 * Installs the given module and returns the module ID, if already installed simply returns the module ID
	 *
	 * @param string $module The WHMCS module name
	 * @param array An array of mapping fields for this particular module (optional, will automatically load if not given)
	 * @return int The ID of the module in Blesta
	 */
	private function installModule($module, $mapping = null) {
		if (!isset($this->ModuleManager))
			Loader::loadModels($this, array("ModuleManager"));
		
		if ($mapping == null)
			$mapping = $this->getModuleMapping($module);
		
		// Return module if already mapped
		if (isset($this->mappings['modules'][$module]))
			return $this->mappings['modules'][$module];
		
		// Check if module is already installed
		$mod = $this->local->select(array("id"))->from("modules")->
			where("company_id", "=", Configure::get("Blesta.company_id"))->
			where("class", "=", $mapping['module'])->fetch();
		if ($mod) {
			$this->mappings['modules'][$module] = $mod->id;
			return $mod->id;
		}
		
		// Install the module since it does not already exist
		$module_id = null;
		
		try {
			$vars = array(
				'company_id' => Configure::get("Blesta.company_id"),
				'class' => $mapping['module']
			);
			$module_id = $this->ModuleManager->add($vars);
		}
		catch (Exception $e) {
			// Module couldn't be added
		}
		$this->mappings['modules'][$module] = (int)$module_id;
		return $module_id;
	}
	
	/**
	 * Adds package pricing
	 *
	 * @param array $pricing A numerically indexed array of pricing info including:
	 * 	- term
	 * 	- period
	 * 	- price
	 * 	- setup_fee
	 * 	- cancel_fee
	 * 	- currency
	 * @param string $package_id The Blesta package ID to add pricing to
	 */
	private function addPackagePricing($pricing, $package_id) {
		// Add package pricing
		$pricing_id = null;
		foreach ($pricing as $price) {
			if (version_compare(BLESTA_VERSION, "3.1.0-b1", ">=")) {
				$vars = $price;
				$vars['company_id'] = Configure::get("Blesta.company_id");
				
				$this->local->insert("pricings", $vars);
				$pricing_id = $this->local->lastInsertId();
				
				$this->local->insert("package_pricing",
					array(
						'package_id' => $package_id,
						'pricing_id' => $pricing_id
					)
				);
				$pricing_id = $this->local->lastInsertId();
			}
			else {
				$vars = $price;
				$vars['package_id'] = $package_id;
				$this->local->insert("package_pricing", $vars);
				$pricing_id = $this->local->lastInsertId();
			}
		}
		return $pricing_id;
	}
	
	/**
	 * Adds package meta for the given package
	 *
	 * @param array $package An array of package info including:
	 * 	- id The ID of the package in WHMCS
	 * 	- * misc package fields
	 * @param array $mapping An array of module mapping config settings
	 */
	private function addPackageMeta($package, $mapping) {
		// Add package meta
		if (isset($mapping['package_meta'])) {
			foreach ($mapping['package_meta'] as $meta) {
				$vars = (array)$meta;
				$vars['package_id'] = $this->mappings['packages'][$package['id']];
				$vars['value'] = null;
				
				if (is_object($meta->value)) {
					if (isset($meta->value->package)) {
						$meta_key = strtolower($meta->value->package);
						if (array_key_exists($meta_key, $package)) {
							$vars['value'] = $package[$meta_key];
							if ($meta_key == "password")
								$vars['value'] = $this->decryptData($package[$meta_key]);
						}
					}
				}
				else
					$vars['value'] = $meta->value;
	
				if (isset($meta->callback))
					$vars['value'] = call_user_func_array($meta->callback, array($vars['value']));
	
				if ($vars['serialized'] == 1)
					$vars['value'] = serialize($vars['value']);
				if ($vars['encrypted'] == 1)
					$vars['value'] = $this->ModuleManager->systemEncrypt($vars['value']);
				
				$this->local->insert("package_meta", $vars, array("package_id", "key", "value", "serialized", "encrypted"));
			}
		}
	}
	
	/**
	 * Adds service fields
	 *
	 * @param array $service An array of key/value pairs for the remote service including:
	 * 	- id The ID of the service on the remote server
	 * 	- * other fields
	 * @param array $mapping An array of module mapping config settings
	 */
	private function addServiceFields($service, $mapping) {
		if (!isset($this->Services))
			Loader::loadModels($this, array("Services"));
			
		foreach ($mapping['service_fields'] as $key => $field) {
			$value = array_key_exists($key, $service) ? $service[$key] : null;
			if ($key == "password" && $value != "")
				$value = $this->decryptData($value);
			
			if (!is_object($field))
				continue;
			
			if (isset($field->callback))
				$value = call_user_func_array($field->callback, array($value));
			
			if ($field->serialized > 0)
				$value = serialize($value);
			if ($field->encrypted > 0)
				$value = $this->Services->systemEncrypt($value);
			
			$vars = array(
				'service_id' => $this->mappings['services'][$service['id']],
				'key' => $field->key,
				'value' => $value != null ? $this->decode($value) : "",
				'serialized' => $field->serialized,
				'encrypted' => $field->encrypted
			);
			$this->local->insert("service_fields", $vars);
		}
	}
	
	/**
	 * Get the pricing for the given term, package, and currency
	 *
	 * @param array $term An array of term info including:
	 * 	- term
	 * 	- period
	 * @param stdClass $package The package in Blesta
	 * @param string $currency The currency to fetch the pricing ID in, will fallback to any currency for the matching term and period
	 * @param string $amount The service amount
	 * @return stdClass An object containing the pricing
	 */
	private function getPricing($term, $package, $currency = null, $amount = null) {
		if (!is_array($term))
			return null;

		$pricing_id = null;		
		if ($package) {
			foreach ($package->pricing as $price) {
				if ($price->term == $term['term'] && $price->period == $term['period']) {
					$pricing_id = $price->id;
					if ($price->currency == $currency) {
						return $price;
					}
				}
			}
		}
		
		// If no pricing found, add default pricing
		$pricing = array(
			array(
				'term' => $term['term'],
				'period' => $term['period'],
				'currency' => $currency ? $currency : "USD",
				'price' => $amount !== null ? $amount : 0,
			)
		);
		$pricing_id = $this->addPackagePricing($pricing, $package->id);
		
		$fields = array("package_pricing.id", "package_pricing.pricing_id", "package_pricing.package_id", "pricings.term",
			"pricings.period", "pricings.price", "pricings.setup_fee",
			"pricings.cancel_fee", "pricings.currency",
			"packages.taxable");
		return $this->local->select($fields)->from("package_pricing")->
			innerJoin("pricings", "pricings.id", "=", "package_pricing.pricing_id", false)->
			innerJoin("packages", "packages.id", "=", "package_pricing.package_id", false)->
			where("package_pricing.id", "=", $pricing_id)->fetch();
	}
	
	/**
	 * Get the pricing for the given term, option value ID, and currency
	 *
	 * @param array $term An array of term info including:
	 * 	- term
	 * 	- period
	 * @param int $value_id The option value ID in Blesta
	 * @param string $currency The currency to fetch the pricing ID in
	 * @return stdClass An object containing the pricing
	 */
	private function getOptionPricing($term, $value_id, $currency) {
		if (!is_array($term))
			return null;

		$fields = array("package_option_pricing.id", "package_option_pricing.pricing_id", "package_option_pricing.option_value_id", "pricings.term",
			"pricings.period", "pricings.price", "pricings.setup_fee",
			"pricings.cancel_fee", "pricings.currency");
		return $this->local->select($fields)->from("package_option_pricing")->
			innerJoin("pricings", "pricings.id", "=", "package_option_pricing.pricing_id", false)->
			where("pricings.currency", "=", $currency)->
			where("pricings.period", "=", $term['period'])->
			where("pricings.term", "=", $term['term'])->
			where("package_option_pricing.option_value_id", "=", $value_id)->fetch();
	}
	
	/**
	 * Returns the local module row ID used for the remote service
	 *
	 * @param int $local_module_id The local ID of the module
	 * @param string $row_value The module row field value for the remote service that uniquely identifies the module row
	 * @param string $remote_module The name of the module on the remote server
	 * @return int The local module row ID for the service
	 */
	private function getModuleRowId($local_module_id, $row_value = null, $remote_module = null) {
		$module_row = false;
		if ($row_value) {
			$module_row = $this->local->select(array("module_rows.*"))->from("module_rows")->
				innerJoin("module_row_meta", "module_row_meta.module_row_id", "=", "module_rows.id", false)->
				where("module_row_meta.value", "=", $row_value)->
				where("module_rows.module_id", "=", $local_module_id)->fetch();
		}
		// If no field, attempt to look up module row based on module name, since
		// the universal module uses the module name to create module rows
		else {
			$module_row = $this->local->select(array("module_rows.*"))->from("module_rows")->
				innerJoin("modules", "modules.id", "=", "module_rows.module_id", false)->
				on("module_row_meta.module_row_id", "=", "module_rows.id", false)->
				innerJoin("module_row_meta", "module_row_meta.key", "=", "name")->
				where("modules.class", "=", "universal_module")->
				where("module_row_meta.value", "=", $remote_module)->
				where("module_rows.module_id", "=", $local_module_id)->fetch();
			
		}
		if ($module_row) {
			return $module_row->id;
		}
		else {
			$module_row = $this->local->select(array("module_rows.*"))->from("module_rows")->
				where("module_rows.module_id", "=", $local_module_id)->fetch();
			return $module_row->id;
		}
	}
	
	/**
	 * Fetch the currency in used by the given client
	 *
	 * @param int $client_id
	 * @return string The currency code for the client
	 */
	private function getCurrency($client_id) {
		if (!isset($this->Clients))
			Loader::loadModels($this, array("Clients"));
			
		static $currencies = array();
		
		if (!isset($currencies[$client_id])) {
			$default_currency = $this->Clients->getSetting($client_id, "default_currency");
			if ($default_currency)
				$currencies[$client_id] = $default_currency->value;
		}
		
		// Get currency this client is invoiced in
		return isset($currencies[$client_id]) ? $currencies[$client_id] : "USD";
	}
	
	/**
	 * Looks for the TLD of the given domain based on the packages created for the given registrar
	 *
	 * @param string $domain
	 * @param string $registrar The registrar
	 * @return string The TLD
	 */
	private function getTld($domain, $registrar) {
		$tld = $domain;
		do {
			$tld = strstr(ltrim($tld, "."), ".");
		}
		while (!isset($this->mappings['packages'][$tld . $registrar]) && $tld != "");
		
		return $tld;
	}
	
	/**
	 * Decodes the HTML entities of the given string
	 *
	 * @param string $str The string to decode
	 * @return string The decoded string
	 */
	protected function decode($str) {
		if ($str === null)
			return $str;
		return html_entity_decode($str, ENT_QUOTES, "UTF-8");
	}
	
	/**
	 * Set debug data
	 *
	 * @param string $str The debug data
	 */
	protected function debug($str) {
		static $set_buffering = false;
		
		if ($this->enable_debug) {
			if (!$set_buffering) {
				ini_set('output_buffering', 'off');
				ini_set('zlib.output_compression', false);
				@ob_end_flush();
				
				ini_set('implicit_flush', true);
				ob_implicit_flush(true);
				
				header("Content-type: text/plain");
				header('Cache-Control: no-cache');
				$set_buffering = true;
			}
			
			echo $str . "\n";
			
			@ob_flush();
			flush();
		}
	}
	
	/**
	 * Start a timer for the given task
	 *
	 * @param string $task
	 */
	protected function startTimer($task) {
		$this->task[$task] = array('start' => microtime(true), 'end' => 0, 'total' => 0);
	}

	/**
	 * Pause a timer for the given task
	 *
	 * @param string $task
	 */	
	protected function pauseTimer($task) {
		$this->task[$task]['end'] = microtime(true);
		$this->task[$task]['total'] += ($this->task[$task]['end'] - $this->task[$task]['start']);
	}
	
	/**
	 * Unpause a timer for the given task
	 *
	 * @param string $task
	 */
	protected function unpauseTimer($task) {
		$this->task[$task]['start'] = microtime(true);
	}
	
	/**
	 * End a timer for the given task, output to debug
	 *
	 * @param string $task
	 */
	protected function endTimer($task) {
		if ($this->task[$task]['start'] > $this->task[$task]['end'])
			$this->pauseTimer($task);
		
		if ($this->enable_debug) {
			$this->debug($task . " took: " . round($this->task[$task]['total'], 4) . " seconds");
		}
	}
}
?>
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Answer this question...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
×
×
  • Create New...