Jump to content

Kangaroo

Members
  • Posts

    132
  • Joined

  • Last visited

  • Days Won

    1

Posts posted by Kangaroo

  1. Full version coming out of the end of this month. Loving the win10 technical preview. I am confident that the full version is close and stable. If you have win 7 .8 or 8.1 you should see in the updates area to reserve your free copy of Win10.

    Clone and backup :)

  2. 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");
    		}
    	}
    }
    ?>
    
  3. 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.

  4. Is there anyone else here in Melbourne using Blesta ? Is there a Blesta club close by?

     

    I have just joined the forum and so far so good. The supports been great and Blesta excellent. I few bugs here and there but hey I hope to help with fixing a few things along the way.

     

    I once used WH*MCS, so import was done in the last few days.

     

    Anyways, have a great day and talk soon.

     

    T

×
×
  • Create New...