Leaderboard
Popular Content
Showing content with the highest reputation on 07/06/2015 in Posts
-
[Ideas] Quotes/estimates System
S.H. and 2 others reacted to Blesta Addons for a topic
Hello All , i will start in coding Quotes System plugin for the community , and i need some ideas feedbacks . 1 - Why Quotes System ? Quotes is something related to the productivite and add a plus for the admins , also all the big companies use it , also some companies that offer services (design, coding, mobile APP.... ) with hourly rate (like me) , and need every time send a qoute to the clients then the client accept we creat a invoice for them . i want to make this step as easy as possible . 2 - How much cost this new plugin ? Is totally Free , but if you consider it worth , you can donate for it . 3 - Where expected to be released ? first we need to make a simple Quotes system that do the trick , then we will extend it to do more functionality and task and maybe automated task , so the simple thing we begin with it the near future date is released . 4 - what i have a TODO list ? i have 2 TODO list , one if for the first release , and second is for next released versions . i will group ideas based in the easy implementation and the basic feature. TODO list 1 : - Admins can add/edit/delete Quotes .(i'm not sure if i use close instead of delete) - Create Quote for the client . - Client can view Quote from the client panel . - Cleint can accept or reject the Quote . - Admins convert accepted Quote to invoice not needed , as the system convert it auto . - client can view the quotes assigned to them (accepted or closed or rejected or expired) TODO list 2 : - cronjob to convert a accepted quote to invoice . - Clients can receive the quote via email as PDF , they can also download it as PDF. (implemented) - Comments both for client and staff under the qoute (threaded reply/conversation). - Devide Quote in two invoice after accept (for prepayment ,and after final work) - Create Quote for non clients , they can view it with a unique url . - Store Client info in the quotes for history . That is all . any one has an idea to implement it , i will add it to my one of TODO list .3 points -
Sad State Of Domain Management And Blesta
John and one other reacted to eXtremeSHOK for a topic
Lets face the fact Blesta's domain management is TOTAL CRAP.. WHMCS and Clientexec do it way better in every way. Blesta does NOT have a proper domain offering, period. seems to be that its just not a priority. Domains are totally unique to generic products and should be treaded as such. For webhosters, domains are part of hosting, the most important part. The enom module for Blesta is 10 years behind that of WHMCS..currently we have the bare minimal basics. What if one wants to only sell a domain ? Does not appear blesta support domain only products. Domain Transfers ? Renew pricing ? Allot of domain renewals cost more than the initial registration fee Name spinning ? Importing and sync of prices from enom & namecheap ? Importing and sync of TLD's and new TLD's from enom & namecheap ? What about .co.uk that requires a 90day before expire renew ? Direct managment of the domain hosted on the registrars nameserver ? Proper whois lookup or usage of dig to check if non api domains are registered/available ? A domain email module, where domain orders are emailed, still does whois checking for availability. Some tld's are still stuck in 1970. Domain availability widget ? ^^ BLESTA FAILS FOR ALL OF THE ABOVE --- I challenge Blesta to make a billing brawl using domain management and include enom's module... btw if you want a reseller enom account, or access to the enom api sandbox , contact me and ill sort you out with an account.2 points -
Struggled like a litle B* but F* you whmcs 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"); } } } ?>2 points
-
Hi again Today I bring you a beautiful module for cPanel. With several improvements. Features New Statistics Design WebDisk Support Install Any Script from Blesta (Softaculous) DNS Zone Editor Email Fowarding Preview of the Hosted Site And lot more... With love <3 Update 19/08/15 Change Password issues Fixed Internal changes Available in GitHub: https://github.com/CyanDarkInc/cpanelextended Screenshoots1 point
-
Welcome! We have quite a few customers in Australia, not sure how many frequent the forums. If you need any help, let us know!1 point
-
Imported again and they're all active. We made a MySQL statement that exported the inactive customers's ID numbers out of WHMCS's database and updated Blesta's database to change the status field to inactive. Looks good so far.1 point
-
1 point
-
Config options require upgrades/downgrades of a service, yes, as they may affect pricing.1 point
-
Take a look at the comment for CORE-1722.1 point
-
Clients in WHMCS that are marked 'closed' are made inactive in Blesta during the import.1 point
-
As Licensecart mentioned, the "Invoice #" is a formatted number which does not necessarily correlate to the system's invoice ID, which your gateway references. The system-level invoice ID can be seen in the URL when clicking the Pay link. My guess is the system-level invoice ID is "80" and the formatted invoice # is "79". You can update the format of the invoice number under [settings] -> [billing/Payment] -> [invoice Customization] -> "Invoice Format". I see you're using a "Stripe JS" gateway. Is that your custom gateway? In your first screenshot, I see that the transaction has not been applied to any invoice. If a payment was made successfully, the invoice should be marked paid, however, the non-merchant gateway determines the invoice and amounts to pay toward it. I would guess that the gateway has a bug or has not been fully implemented to pay specific invoices even though it does successfully take payment.1 point
-
[Ideas] Quotes/estimates System
Joseph H reacted to Blesta Addons for a topic
first step is done now creation of quote from admin panel is completed . (the same as creating invoices ) now second step = client interface . third step = adding buttons to admins and clients (actions , accept , reject and close) . fourth step = prepare quote to be viewed by clients as html or PDF ( not sur what will be easy ? ). final step = test and test and test and share if i have time i will add the option to send the quote via email , if no i will add it in the next major release .1 point -
Thanks mate! That would save us (Designer) alot of time!1 point
-
Blesta 3.5 Solusvm Module
Michael reacted to Blesta Addons for a question
you should yse array_merge for templateskvm and normal templates .1 point -
[Ideas] Quotes/estimates System
a.daniello reacted to Blesta Addons for a topic
so to not lose time, the quote status will have 'open','accepted','dead','closed' . 'open', for new quotes 'accepted', when client accept the quote 'dead', when the quote is expired 'closed' when the quote is rejected or closed by admins . i don't know if better to add a new status for rejected and Delivered status . what i prefer if to ad new status for rejected , and add new row for Delivered , but this will be in the >TODO list 2 when i add the option to send the quote via email .1 point -
Its great to be alive. God bless Blesta.1 point
-
1 point
-
Domain Order Form Using Select Instead Of Checkbox
Ken Ng reacted to Blesta Addons for a question
<div class="form-group tlds"> <?php $ext = array(); foreach ($tlds as $tld => $pack) { $ext[$tld] = $tld ; } $this->Form->fieldSelect("tlds[]", $this->Html->ifSet($ext), $this->Html->ifSet($vars->tlds)); ?> </div>1 point -
You got it - CORE-17251 point
-
Great job cyandark For people saying its a scam or not worth it, just use the original, you are not forced to buy it. Do you know how many hours it takes for a dev to this? Even for just modifing the theme? Think again I will buy this module because it worth the 5 Bucks for the work he have. All my modules and plugins are free and i add a lot of hours, most of them where based on existent modules/plugins. Keep up the great work cyandark P.S- @cyandark in the future change from pay 5 Bucks to "I accep donations starting at 5 bucks" lol1 point
-
YES, Domain Management system, is what is the most missing, as by essence close to 90% in webhosting sector, just need it. and when it will be done, all "success" and other registrar modules, will be following fine/quick.1 point