argument('dbname'); $host = $this->askOrUseENV(argument: 'host', env: 'DB_HOST', question: 'Enter the host:', placeholder: 'localhost'); $port = $this->askOrUseENV(argument: 'port', env: 'DB_PORT', question: 'Enter the port:', placeholder: '3306'); $username = $this->askOrUseENV(argument: 'username', env: 'DB_USERNAME', question: 'Enter the username:', placeholder: 'paymenter'); $password = password("Enter the password for user '$username':", required: true); try { $this->pdo = new PDO("mysql:host=$host;port=$port;dbname=$dbname", $username, $password); $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); $this->info('Connected to WHMCS database, Starting migration...'); $this->prepareDatabase(); DB::statement('SET foreign_key_checks=0'); // Remove default currencies DB::table('currencies')->truncate(); // Import currencies $this->importCurrencies(); $this->importUsers(); $this->importAdmins(); $this->importCategories(); $this->importConfigOptions(); $this->importProducts(); $this->importTickets(); $this->importOrders(); $this->importServices(); $this->importCancellations(); $this->importInvoices(); $this->importInvoiceItems(); $this->importPayments(); DB::statement('SET foreign_key_checks=1'); SettingsProvider::flushCache(); } catch (PDOException $e) { $this->fail('Connection failed: ' . $e->getMessage()); } } private function prepareDatabase() { // Rerun migrations $this->call('migrate:fresh', ['--force' => true]); // Seed default data $this->call('db:seed', ['--force' => true]); $this->call('db:seed', ['--class' => 'CustomPropertySeeder', '--force' => true]); } protected function askOrUseENV(string $argument, string $env, string $question, string $placeholder): string { $arg_value = $this->argument($argument); if ($arg_value) { return $arg_value; } $env_value = env($env); if (!is_null($env_value) && $env_value !== '') { return $env_value; } return text($question, required: true, placeholder: $placeholder); } protected function migrateInBatch(string $table, string $query, Closure $processor) { /** * @var PDOStatement $stmt */ $stmt = $this->pdo->prepare($query); $offset = 0; $stmt->bindValue(':offset', $offset, PDO::PARAM_INT); $stmt->bindValue(':limit', $this->batchSize, PDO::PARAM_INT); $stmt->execute(); $nRows = $this->pdo->query('SELECT COUNT(*) FROM ' . $table)->fetchColumn(); if ($nRows <= $this->batchSize) { $records = $stmt->fetchAll(PDO::FETCH_ASSOC); try { $processor($records); } catch (Throwable $th) { Log::error($th); $this->fail($th->getMessage()); } } else { $bar = $this->output->createProgressBar(round($nRows / $this->batchSize)); $bar->setFormat("Batch: %current%/%max% [%bar%] %percent:3s%%\n"); $bar->start(); while ($records = $stmt->fetchAll(PDO::FETCH_ASSOC)) { $offset += $this->batchSize; $stmt->bindValue(':offset', $offset, PDO::PARAM_INT); $stmt->execute(); try { $processor($records); } catch (Throwable $th) { Log::error($th); $this->fail($th->getMessage()); } $bar->advance(); } $bar->finish(); } $this->info('Done.'); } private function count(string $table): int { $stmt = $this->pdo->prepare('SELECT COUNT(*) as count FROM ' . $table); $stmt->execute(); $result = $stmt->fetch(PDO::FETCH_ASSOC); return (int) ($result['count'] ?? 0); } private function importCurrencies() { $this->info('Importing currencies... (' . $this->count('tblcurrencies') . ' records)'); $this->migrateInBatch('tblcurrencies', 'SELECT * FROM tblcurrencies LIMIT :limit OFFSET :offset', function ($records) { $data = []; foreach ($records as $record) { $format = match ($record['format']) { 1 => '1 000.00', // 1234.56 2 => '1,000.00', // 1,234.56 4 => '1 000,00', // 1,234 default => '1.000,00', // 1.234,56 }; $data[] = [ 'name' => $record['code'], 'code' => $record['code'], 'prefix' => $record['prefix'], 'suffix' => $record['suffix'], 'format' => $format, ]; if ($record['default'] == 1) { // Set default currency Setting::updateOrCreate( ['key' => 'default_currency'], ['value' => $record['code']] ); } } DB::table('currencies')->insert($data); }); } private function importUsers() { $this->info('Importing users... (' . $this->count('tblclients') . ' records)'); $customProperties = CustomProperty::where('model', User::class)->get()->keyBy('key'); $this->migrateInBatch('tblclients', 'SELECT * FROM tblclients LIMIT :limit OFFSET :offset', function ($records) use ($customProperties) { $data = []; $properties = []; $credits = []; foreach ($records as $record) { // Get client (join tblusers_clients on tblusers.id = tblusers_clients.auth_user_id and tblusers_clients.owner = 1) $stmt = $this->pdo->prepare('SELECT * FROM tblusers WHERE id = (SELECT auth_user_id FROM tblusers_clients WHERE client_id = :client_id AND owner = 1 LIMIT 1)'); $stmt->bindValue(':client_id', $record['id'], PDO::PARAM_INT); $stmt->execute(); $user = $stmt->fetch(PDO::FETCH_ASSOC); if (!$user) { continue; } $data[] = [ 'id' => $record['id'], 'first_name' => $record['firstname'], 'last_name' => $record['lastname'], 'email' => $record['email'], 'password' => $user['password'], 'email_verified_at' => $user['email_verified_at'] ? $user['email_verified_at'] : null, 'updated_at' => $record['updated_at'], 'created_at' => $record['created_at'], ]; // Custom properties foreach ($customProperties as $key => $property) { // address1 -> address, companyname -> company_name $whmcsKey = match ($key) { 'address' => 'address1', 'company_name' => 'companyname', 'postcode' => 'zip', 'phonenumber' => 'phone', default => $key, }; if (isset($record[$key]) && $record[$key] !== '') { array_push($properties, [ 'key' => $key, 'value' => $record[$whmcsKey], 'model_id' => $record['id'], 'model_type' => User::class, 'name' => $property->name, 'custom_property_id' => $property->id, 'created_at' => now(), 'updated_at' => now(), ]); } } // Credits if ($record['credit'] > 0) { $currency = $this->pdo->prepare('SELECT * FROM tblcurrencies WHERE id = :id LIMIT 1'); $currency->bindValue(':id', $record['currency'], PDO::PARAM_INT); $currency->execute(); $currency = $currency->fetch(PDO::FETCH_ASSOC); array_push($credits, [ 'user_id' => $record['id'], 'amount' => $record['credit'], 'currency_code' => $currency['code'] ?? null, 'created_at' => now(), 'updated_at' => now(), ]); } } DB::table('users')->insert($data); if (count($properties) > 0) { DB::table('properties')->insert($properties); } if (count($credits) > 0) { DB::table('credits')->insert($credits); } }); } private function importAdmins() { $this->info('Importing admins... (' . $this->count('tbladmins') . ' records)'); $this->migrateInBatch('tbladmins', 'SELECT * FROM tbladmins LIMIT :limit OFFSET :offset', function ($records) { $data = []; foreach ($records as $record) { if ($record['roleid'] != 1 || $record['disabled'] == 1 || DB::table('users')->where('email', $record['email'])->exists()) { // Only import administrators continue; } $data[] = [ 'id' => $record['id'], 'first_name' => $record['firstname'], 'last_name' => $record['lastname'], 'email' => $record['email'], 'password' => $record['password'], 'role_id' => 1, // Admin role 'email_verified_at' => null, 'created_at' => $record['created_at'], 'updated_at' => $record['lastlogin'] ?? now(), ]; } }); } private function importCategories() { $this->info('Importing categories... (' . $this->count('tblproductgroups') . ' records)'); $this->migrateInBatch('tblproductgroups', 'SELECT * FROM tblproductgroups LIMIT :limit OFFSET :offset', function ($records) { $data = []; foreach ($records as $record) { $data[] = [ 'id' => $record['id'], 'name' => $record['name'], 'description' => $record['tagline'], 'sort' => $record['order'], 'slug' => $record['slug'], 'created_at' => $record['created_at'], 'updated_at' => $record['updated_at'], ]; } DB::table('categories')->insert($data); }); } private function importConfigOptions() { $this->info('Importing config options... (' . $this->count('tblproductconfigoptions') . ' records)'); $this->migrateInBatch('tblproductconfigoptions', 'SELECT * FROM tblproductconfigoptions LIMIT :limit OFFSET :offset', function ($records) { $data = []; $options = []; $planData = []; $priceData = []; foreach ($records as $record) { if (strpos($record['optionname'], '|') !== false) { $environmentVariable = explode('|', $record['optionname'])[0]; $name = explode('|', $record['optionname'])[1] ?? $record['optionname']; } else { $environmentVariable = null; $name = $record['optionname']; } $data[] = [ 'id' => $record['id'], 'name' => $name, 'env_variable' => $environmentVariable, 'type' => match ($record['optiontype']) { 1 => 'select', 2 => 'radio', 3 => 'checkbox', 4 => 'number', default => 'select', }, 'sort' => $record['order'], 'hidden' => $record['hidden'], 'created_at' => now(), 'updated_at' => now(), ]; // Get options $stmt = $this->pdo->prepare('SELECT * FROM tblproductconfigoptionssub WHERE configid = :configid'); $stmt->bindValue(':configid', $record['id'], PDO::PARAM_INT); $stmt->execute(); $optionRecords = $stmt->fetchAll(PDO::FETCH_ASSOC); foreach ($optionRecords as $option) { if (strpos($option['optionname'], '|') !== false) { $environmentVariable = explode('|', $option['optionname'])[0]; $name = explode('|', $option['optionname'])[1] ?? $option['optionname']; } else { $environmentVariable = null; $name = $option['optionname']; } $options[] = [ 'id' => $record['id'] . $option['id'], 'parent_id' => $option['configid'], 'name' => $name, 'env_variable' => $environmentVariable, 'sort' => $option['sortorder'], 'created_at' => now(), 'updated_at' => now(), ]; // Get pricing for the option $stmt2 = $this->pdo->prepare('SELECT * FROM tblpricing WHERE type = "configoptions" AND relid = :relid'); $stmt2->bindValue(':relid', $option['id'], PDO::PARAM_INT); $stmt2->execute(); $prices = $stmt2->fetchAll(PDO::FETCH_ASSOC); $this->priceMagic($prices, $planData, $priceData, ['id' => $record['id'] . $option['id'], 'paytype' => 'recurring'], ConfigOption::class); } } DB::table('config_options')->insert($data); if (count($options) > 0) { DB::table('config_options')->insert($options); } // Insert plans and then prices foreach ($planData as $planKey => $plan) { $planId = DB::table('plans')->insertGetId($plan); if (isset($priceData[$planKey])) { foreach ($priceData[$planKey] as &$price) { $price['plan_id'] = $planId; } DB::table('prices')->insert($priceData[$planKey]); } } }); } private function priceMagic(&$prices, &$planData, &$priceData, $record, $priceableType = Product::class) { foreach ($prices as $pricing) { if ($record['paytype'] === 'onetime') { // One-time payment product, create a one-time plan $setupFee = $pricing['msetupfee'] ?? 0; // Create a unique key to link plan and price $planKey = $record['id'] . '_onetime'; $planData[$planKey] = [ 'priceable_id' => $record['id'], 'priceable_type' => $priceableType, 'name' => 'One-Time', 'type' => 'one-time', 'billing_period' => 0, 'billing_unit' => null, ]; $currency = $this->pdo->prepare('SELECT * FROM tblcurrencies WHERE id = :id LIMIT 1'); $currency->bindValue(':id', $pricing['currency'], PDO::PARAM_INT); $currency->execute(); $currency = $currency->fetch(PDO::FETCH_ASSOC); $priceData[$planKey][] = [ 'currency_code' => $currency['code'], 'price' => $pricing['monthly'], 'setup_fee' => $setupFee, ]; continue; } foreach (['monthly', 'quarterly', 'semiannually', 'annually', 'biennially', 'triennially'] as $period) { if ($pricing[$period] > 0) { $setupFee = match ($period) { 'monthly' => $pricing['msetupfee'], 'quarterly' => $pricing['qsetupfee'], 'semiannually' => $pricing['ssetupfee'], 'annually' => $pricing['asetupfee'], 'biennially' => $pricing['bsetupfee'], 'triennially' => $pricing['tsetupfee'], default => 0, }; // Create a unique key to link plan and price $planKey = $record['id'] . '_' . $period; $planData[$planKey] = [ 'priceable_id' => $record['id'], 'priceable_type' => $priceableType, 'name' => ucfirst($period), 'type' => 'recurring', 'billing_period' => match ($period) { 'monthly' => 1, 'quarterly' => 3, 'semiannually' => 6, 'annually' => 1, 'biennially' => 2, 'triennially' => 3, default => 1, }, 'billing_unit' => match ($period) { 'monthly', 'quarterly', 'semiannually' => 'month', 'annually', 'biennially', 'triennially' => 'year', default => 'month', }, ]; $currency = $this->pdo->prepare('SELECT * FROM tblcurrencies WHERE id = :id LIMIT 1'); $currency->bindValue(':id', $pricing['currency'], PDO::PARAM_INT); $currency->execute(); $currency = $currency->fetch(PDO::FETCH_ASSOC); $priceData[$planKey][] = [ 'currency_code' => $currency['code'], 'price' => $pricing[$period], 'setup_fee' => $setupFee, ]; } } } } private function importProducts() { $this->info('Importing products... (' . $this->count('tblproducts') . ' records)'); $this->migrateInBatch('tblproducts', 'SELECT * FROM tblproducts LIMIT :limit OFFSET :offset', function ($records) { $data = []; $planData = []; $priceData = []; $upgrades = []; foreach ($records as $record) { $data[] = [ 'id' => $record['id'], 'category_id' => $record['gid'], 'name' => $record['name'], 'description' => $record['description'], 'slug' => !empty($record['slug']) ? $record['slug'] : \Str::slug($record['name']), 'hidden' => $record['hidden'], 'stock' => $record['stockcontrol'] ? $record['qty'] : null, 'allow_quantity' => match ($record['allowqty']) { 1 => 'separated', 3 => 'combined', default => 'disabled', }, 'created_at' => $record['created_at'], 'updated_at' => $record['updated_at'], ]; // Upgrades $stmt = $this->pdo->prepare('SELECT * FROM tblproduct_upgrade_products WHERE product_id = :product_id'); $stmt->bindValue(':product_id', $record['id'], PDO::PARAM_INT); $stmt->execute(); $upgradeRecords = $stmt->fetchAll(PDO::FETCH_ASSOC); foreach ($upgradeRecords as $upgrade) { $upgrades[] = [ 'product_id' => $upgrade['product_id'], 'upgrade_id' => $upgrade['upgrade_product_id'], 'created_at' => now(), 'updated_at' => now(), ]; } // Config options $stmt = $this->pdo->prepare('SELECT * FROM tblproductconfiglinks WHERE pid = :pid'); $stmt->bindValue(':pid', $record['id'], PDO::PARAM_INT); $stmt->execute(); $configOptionRecords = $stmt->fetchAll(PDO::FETCH_ASSOC); foreach ($configOptionRecords as $configOptionGroupId) { // Get the config option group $configOptionGroup = $this->pdo->prepare('SELECT * FROM tblproductconfiggroups WHERE id = :id LIMIT 1'); $configOptionGroup->bindValue(':id', $configOptionGroupId['gid'], PDO::PARAM_INT); $configOptionGroup->execute(); $configOptionGroup = $configOptionGroup->fetch(PDO::FETCH_ASSOC); if (!$configOptionGroup) { continue; } // Get config options in the group $configOptions = $this->pdo->prepare('SELECT * FROM tblproductconfigoptions WHERE gid = :gid'); $configOptions->bindValue(':gid', $configOptionGroup['id'], PDO::PARAM_INT); $configOptions->execute(); $configOptions = $configOptions->fetchAll(PDO::FETCH_ASSOC); foreach ($configOptions as $configOption) { // Link config option to product DB::table('config_option_products')->insert([ 'product_id' => $record['id'], 'config_option_id' => $configOption['id'], ]); } } } // Insert products first DB::table('products')->insert($data); // Insert upgrades if (count($upgrades) > 0) { DB::table('product_upgrades')->insert($upgrades); } // Now process plans for all products in this batch foreach ($records as $record) { if ($record['paytype'] === 'free') { // Free product, create a free plan $planData[$record['id'] . '_free'] = [ 'priceable_id' => $record['id'], 'priceable_type' => Product::class, 'name' => 'Free', 'type' => 'free', ]; continue; } $stmt = $this->pdo->prepare('SELECT * FROM tblpricing WHERE type = "product" AND relid = :relid'); $stmt->bindValue(':relid', $record['id'], PDO::PARAM_INT); $stmt->execute(); $prices = $stmt->fetchAll(PDO::FETCH_ASSOC); $this->priceMagic($prices, $planData, $priceData, $record); } // Insert plans and then prices foreach ($planData as $planKey => $plan) { $planId = DB::table('plans')->insertGetId($plan); if (isset($priceData[$planKey])) { foreach ($priceData[$planKey] as &$price) { $price['plan_id'] = $planId; } DB::table('prices')->insert($priceData[$planKey]); } } }); } private function getUserIdTicket($message, &$userId) { if (!$userId) { if ($message['admin'] !== '') { // Admin is a first name + last name in WHMCS $user = DB::table('users') ->where(DB::raw("CONCAT(first_name, ' ', last_name)"), $message['admin']) ->orWhere('first_name', $message['admin']) ->first(); if ($user) { $userId = $user->id; } // If not, we will make a admin account where we will attach all admin messages if (!$userId) { $adminUserId = DB::table('users')->insertGetId([ 'first_name' => $message['admin'], 'last_name' => '', 'email' => 'admin+' . strtolower(str_replace(' ', '_', $message['admin'])) . '@paymenter.org', 'password' => bcrypt(\Str::random(16)), 'created_at' => now(), 'updated_at' => now(), ]); $userId = $adminUserId; } } if (!$userId && DB::table('users')->where('email', $message['email'])->exists()) { $userRecord = DB::table('users')->where('email', $message['email'])->first(); $userId = $userRecord->id; } if (!$userId) { // Create a user with the email $newUserId = DB::table('users')->insertGetId([ 'first_name' => $message['name'], 'last_name' => '', 'email' => $message['email'], 'password' => bcrypt(\Str::random(16)), 'created_at' => now(), 'updated_at' => now(), ]); $userId = $newUserId; } } return $userId; } private function importTickets() { $this->info('Importing tickets... (' . $this->count('tbltickets') . ' records)'); $this->migrateInBatch('tbltickets', 'SELECT * FROM tbltickets LIMIT :limit OFFSET :offset', function ($records) { $data = []; $messages = []; foreach ($records as $record) { // Get user $user = $this->getUserIdTicket($record, $record['userid']); if (!$user) { continue; } $departmentStmt = $this->pdo->prepare('SELECT * FROM tblticketdepartments WHERE id = :id LIMIT 1'); $departmentStmt->bindValue(':id', $record['did'], PDO::PARAM_INT); $departmentStmt->execute(); $department = $departmentStmt->fetch(PDO::FETCH_ASSOC); if ($department) { // You can map department to category if needed $departmentName = $department['name']; } $data[] = [ 'id' => $record['id'], 'user_id' => $user, 'subject' => $record['title'], 'status' => match ($record['status']) { 'Answered' => 'replied', 'Closed' => 'closed', default => 'open', }, 'priority' => match ($record['urgency']) { 'Low' => 'low', 'Medium' => 'medium', 'High' => 'high', default => 'medium', }, 'department' => $departmentName ?? null, 'created_at' => $record['date'], 'updated_at' => $record['lastreply'], ]; // Get ticket messages $stmt = $this->pdo->prepare('SELECT * FROM tblticketreplies WHERE tid = :tid ORDER BY date ASC'); $stmt->bindValue(':tid', $record['id'], PDO::PARAM_INT); $stmt->execute(); $messageRecords = $stmt->fetchAll(PDO::FETCH_ASSOC); foreach ($messageRecords as $message) { // Get admin user if any $userId = $this->getUserIdTicket($message, $message['userid']); $messages[] = [ 'ticket_id' => $record['id'], 'user_id' => $userId, 'message' => $message['message'], 'created_at' => $message['date'], 'updated_at' => $message['date'], ]; } } DB::table('tickets')->insert($data); if (count($messages) > 0) { DB::table('ticket_messages')->insert($messages); } }); } private function importOrders() { $this->info('Importing orders... (' . $this->count('tblorders') . ' records)'); $this->migrateInBatch('tblorders', 'SELECT * FROM tblorders LIMIT :limit OFFSET :offset', function ($records) { $data = []; foreach ($records as $record) { // Get currency from user $user = $this->pdo->prepare('SELECT * FROM tblcurrencies WHERE id = (SELECT currency FROM tblclients WHERE id = :client_id LIMIT 1) LIMIT 1'); $user->bindValue(':client_id', $record['userid'], PDO::PARAM_INT); $user->execute(); $user = $user->fetch(PDO::FETCH_ASSOC); if (!$user) { continue; } $data[] = [ 'id' => $record['id'], 'user_id' => $record['userid'], 'currency_code' => $user['code'], 'created_at' => $record['date'], 'updated_at' => $record['date'], ]; } DB::table('orders')->insert($data); }); } private function importServices() { $this->info('Importing services... (' . $this->count('tblhosting') . ' records)'); $this->migrateInBatch('tblhosting', 'SELECT * FROM tblhosting LIMIT :limit OFFSET :offset', function ($records) { $data = []; foreach ($records as $record) { // Get currency from user $user = $this->pdo->prepare('SELECT * FROM tblcurrencies WHERE id = (SELECT currency FROM tblclients WHERE id = :client_id LIMIT 1) LIMIT 1'); $user->bindValue(':client_id', $record['userid'], PDO::PARAM_INT); $user->execute(); $user = $user->fetch(PDO::FETCH_ASSOC); if (!$user) { continue; } $planId = DB::table('plans') ->where('priceable_id', $record['packageid']) ->where('priceable_type', Product::class) ->where('name', match ($record['billingcycle']) { 'Monthly' => 'Monthly', 'Quarterly' => 'Quarterly', 'Semi-Annually' => 'Semiannually', 'Annually' => 'Annually', 'Biennially' => 'Biennially', 'Triennially' => 'Triennially', 'One Time' => 'One-Time', 'Free Account' => 'Free', default => 'Monthly', }) ->first()?->id; $data[] = [ 'id' => $record['id'], 'order_id' => $record['orderid'], 'product_id' => $record['packageid'], 'status' => match ($record['domainstatus']) { 'Active' => 'active', 'Suspended' => 'suspended', 'Terminated' => 'cancelled', 'Cancelled' => 'cancelled', 'Fraud' => 'cancelled', 'Pending' => 'pending', default => 'pending', }, 'price' => $record['amount'], 'quantity' => $record['qty'], 'user_id' => $record['userid'], 'plan_id' => $planId, 'currency_code' => $user['code'], 'expires_at' => $record['nextduedate'] != '0000-00-00' ? $record['nextduedate'] : null, 'created_at' => $record['regdate'], 'updated_at' => $record['regdate'], ]; } DB::table('services')->insert($data); }); } private function importCancellations() { $this->info('Importing cancellations... (' . $this->count('tblcancelrequests') . ' records)'); $this->migrateInBatch('tblcancelrequests', 'SELECT * FROM tblcancelrequests LIMIT :limit OFFSET :offset', function ($records) { $data = []; foreach ($records as $record) { $data[] = [ 'id' => $record['id'], 'service_id' => $record['relid'], 'reason' => $record['reason'], 'type' => $record['type'] == 'End of Billing Period' ? 'end_of_period' : 'immediate', 'created_at' => $record['date'], 'updated_at' => $record['date'], ]; } DB::table('service_cancellations')->insert($data); }); } private function importInvoices() { $this->info('Importing invoices... (' . $this->count('tblinvoices') . ' records)'); $this->migrateInBatch('tblinvoices', 'SELECT * FROM tblinvoices LIMIT :limit OFFSET :offset', function ($records) { $data = []; foreach ($records as $record) { // Get currency from user $user = $this->pdo->prepare('SELECT * FROM tblcurrencies WHERE id = (SELECT currency FROM tblclients WHERE id = :client_id LIMIT 1) LIMIT 1'); $user->bindValue(':client_id', $record['userid'], PDO::PARAM_INT); $user->execute(); $user = $user->fetch(PDO::FETCH_ASSOC); if (!$user) { continue; } $data[] = [ 'id' => $record['id'], 'number' => !empty($record['invoicenum']) ? $record['invoicenum'] : $record['id'], 'user_id' => $record['userid'], 'status' => match ($record['status']) { 'Paid' => 'paid', 'Unpaid' => 'unpaid', 'Cancelled' => 'cancelled', 'Refunded' => 'cancelled', default => 'unpaid', }, 'currency_code' => $user['code'], 'created_at' => $record['date'], 'updated_at' => $record['date'], ]; } DB::table('invoices')->insert($data); }); // Set invoice number in settings to highest imported invoice number + 1 $highestInvoiceNumber = DB::table('invoices')->max('number'); Setting::updateOrCreate( ['key' => 'invoice_number'], ['value' => $highestInvoiceNumber + 1] ); } private function importInvoiceItems() { $this->info('Importing invoice items... (' . $this->count('tblinvoiceitems') . ' records)'); $this->migrateInBatch('tblinvoiceitems', 'SELECT * FROM tblinvoiceitems LIMIT :limit OFFSET :offset', function ($records) { $data = []; foreach ($records as $record) { $data[] = [ 'id' => $record['id'], 'invoice_id' => $record['invoiceid'], 'description' => $record['description'], 'price' => $record['amount'], 'created_at' => now(), 'updated_at' => now(), 'reference_type' => match ($record['type']) { 'Hosting' => Service::class, 'Domain' => null, 'Addon' => null, 'Upgrade' => null, default => null, }, 'reference_id' => $record['relid'], ]; } DB::table('invoice_items')->insert($data); }); } private function importPayments() { $this->info('Importing payments... (' . $this->count('tblaccounts') . ' records)'); $this->migrateInBatch('tblaccounts', 'SELECT * FROM tblaccounts LIMIT :limit OFFSET :offset', function ($records) { $data = []; foreach ($records as $record) { // Get currency from user $user = $this->pdo->prepare('SELECT * FROM tblcurrencies WHERE id = (SELECT currency FROM tblclients WHERE id = :client_id LIMIT 1) LIMIT 1'); $user->bindValue(':client_id', $record['userid'], PDO::PARAM_INT); $user->execute(); $user = $user->fetch(PDO::FETCH_ASSOC); if (!$user) { continue; } $data[] = [ 'id' => $record['id'], 'invoice_id' => $record['invoiceid'], 'amount' => $record['amountin'], 'transaction_id' => $record['transid'], 'created_at' => $record['date'], 'updated_at' => $record['date'], ]; } DB::table('invoice_transactions')->insert($data); }); } }