/** * Execute the console command. * * @return mixed */ public function fire() { // This process isn't necessary for resellers if ('true' == env('RESELLER')) { dd('We are a reseller.'); } // Grab all new events concerning virtual machine creation or service offering changes. $eventId = Config::firstOrCreate(['parameter' => 'lastEventId']); if (null == $eventId->data) { $eventId->data = 0; } // Initialize the parameter with a value. $this->info('Last Processed Event ID is: [' . $eventId->data . ']'); $maxEventId = UsageEvent::max('id'); $this->info('Max ID in Event table: [' . $maxEventId . ']'); if ($eventId->data == $maxEventId) { // If there are no new events, no processing is required. exit; } $events = UsageEvent::where('id', '>', $eventId->data)->where('id', '<=', $maxEventId)->where(function ($query) { $query->where('type', '=', 'VM.CREATE')->orWhere('type', '=', 'VM.UPGRADE'); })->get(); // Iterate over each event and process $events->each(function ($event) { // In this context: // resource_id = vm_instance_id // type = Event type // offering_id = service_offering_id // Grab the Service Offering of the VM the event concerns if it is customized. $this->info('VM_ID: [' . $event->resource_id . '] Type: [' . $event->type . '] SO_ID: [' . $event->offering_id . ']'); $so = DB::connection('cloud')->table('service_offering')->whereId($event->offering_id)->whereNull('cpu')->whereNull('speed')->whereNull('ram_size')->first(); // If we have a result, the Service Offering is custom. if (isset($so->id)) { $this->info('We have a custom service offering'); // We need to grab the custom fields for this VM. $vmDetails = \App\VmInstance::find($event->resource_id)->details->toArray(); $resources = array(); foreach ($vmDetails as $detail) { if ('cpuNumber' == $detail->name || 'cpuSpeed' == $detail->name || 'memory' == $detail->name) { $resources["{$detail->name}"] = $detail->value; } } $resources['vmInstanceId'] = $event->resource_id; VmResources::create($resources); } }); // Set the last processed record ID so we don't go over records multiple times. $eventId->data = $maxEventId; $eventId->save(); }
/** * Execute the console command. * * @return mixed */ public function fire() { // Setup lists to look up ACS IDs. // We need: zones, accounts, domains, instances, service offerings and templates. $dataTypes = ['cloud.data_center' => 'zones', 'cloud.domain' => 'domains', 'cloud.account' => 'accounts', 'cloud.vm_instance' => 'instances', 'cloud.service_offering_view' => 'serviceofferings', 'cloud.template_view' => 'templates']; $uuidLookup = []; foreach ($dataTypes as $table => $type) { $dbInfo = DB::table($table)->get(); $uuidLookup[$type] = []; foreach ($dbInfo as $info) { $uuidLookup[$type][$info->id] = $info->uuid; } } $diskInfo = DB::table('cloud.disk_offering')->get(); $diskOfferings = []; foreach ($diskInfo as $do) { $diskOfferings[$do->id] = $do->tags; } // Grab yesterday's records $yesterday = date('Y-m-d', time() - 84600); $records = App\CloudUsage::like('start_date', $yesterday . '%')->billable()->get()->each(function ($record) use($uuidLookup, $diskOfferings) { $recordType = ''; switch ($record->usage_type) { case 2: $recordType = 'VM Instance'; // Grab the service offering of the VM $serviceOffering = App\ServiceOffering::find($record->offering_id); if (null == $serviceOffering->cpu && null == $serviceOffering->speed && null == $serviceOffering->ram_size) { // Our service offering is Custom. Grab its resources from our custom table. $resources = App\VmResources::find($record->vm_instance_id); $cpuNumber = $resources->cpuNumber; $cpuSpeed = $resources->cpuSpeed; $memory = $resources->memory; } else { $cpuNumber = $serviceOffering->cpu; $cpuSpeed = $serviceOffering->speed; $memory = $serviceOffering->ram_size; } App\UsageVm::create(['zoneId' => $uuidLookup['zones'][$record->zone_id], 'accountId' => $uuidLookup['accounts'][$record->account_id], 'domainId' => $uuidLookup['domains'][$record->domain_id], 'vm_name' => $record->vm_name, 'type' => 'VM', 'usage' => $record->raw_usage, 'vmInstanceId' => $uuidLookup['instances'][$record->vm_instance_id], 'serviceOfferingId' => $uuidLookup['serviceofferings'][$record->offering_id], 'templateId' => $uuidLookup['templates'][$record->template_id], 'cpuNumber' => $cpuNumber, 'cpuSpeed' => $cpuSpeed, 'memory' => $memory, 'startDate' => $record->start_date, 'endDate' => $record->end_date]); break; case 4: case 5: $recordType = 'Network Usage'; App\UsageGeneral::create(['zoneId' => $uuidLookup['zones'][$record->zone_id], 'accountId' => $uuidLookup['accounts'][$record->account_id], 'domainId' => $uuidLookup['domains'][$record->domain_id], 'type' => stripos($record->usage_display, 'Received') === false ? 'Network Sent' : 'Network Received', 'usage' => $record->raw_usage, 'startDate' => $record->start_date, 'endDate' => $record->end_date]); break; case 6: $recordType = 'Disk/Volume'; // Fetch the VM instance this volume belongs to. $vol = App\Volume::find($record->usage_id); try { $instance = App\VmInstance::find($vol->instance_id); } catch (\Exception $e) { continue; // Ignored bad record. } if (!$instance instanceof App\VmInstance) { continue; } // Ignore this record, its bad for some reason. App\UsageDisk::create(['zoneId' => $uuidLookup['zones'][$record->zone_id], 'accountId' => $uuidLookup['accounts'][$record->account_id], 'domainId' => $uuidLookup['domains'][$record->domain_id], 'volumeId' => $vol->uuid, 'type' => $vol->volume_type == 'ROOT' ? 'Root Volume' : 'Volume', 'tags' => $diskOfferings[$vol->disk_offering_id], 'usage' => $record->raw_usage, 'size' => $record->size, 'vmInstanceId' => $uuidLookup['instances'][$instance->id], 'startDate' => $record->start_date, 'endDate' => $record->end_date]); break; case 9: $recordType = 'Snapshot'; App\UsageDisk::create(['zoneId' => $uuidLookup['zones'][$record->zone_id], 'accountId' => $uuidLookup['accounts'][$record->account_id], 'domainId' => $uuidLookup['domains'][$record->domain_id], 'type' => 'Snapshot', 'usage' => $record->raw_usage, 'size' => $record->size, 'startDate' => $record->start_date, 'endDate' => $record->end_date]); break; case 11: $recordType = 'Load Balancer'; App\UsageGeneral::create(['zoneId' => $uuidLookup['zones'][$record->zone_id], 'accountId' => $uuidLookup['accounts'][$record->account_id], 'domainId' => $uuidLookup['domains'][$record->domain_id], 'type' => 'LB', 'usage' => $record->raw_usage, 'vmInstanceId' => $uuidLookup['instances'][$record->vm_instance_id], 'templateId' => $uuidLookup['templates'][$record->template_id], 'startDate' => $record->start_date, 'endDate' => $record->end_date]); break; case 12: $recordType = 'Port Forward'; App\UsageGeneral::create(['zoneId' => $uuidLookup['zones'][$record->zone_id], 'accountId' => $uuidLookup['accounts'][$record->account_id], 'domainId' => $uuidLookup['domains'][$record->domain_id], 'type' => 'PF', 'usage' => $record->raw_usage, 'vmInstanceId' => $uuidLookup['instances'][$record->vm_instance_id], 'templateId' => $uuidLookup['templates'][$record->template_id], 'startDate' => $record->start_date, 'endDate' => $record->end_date]); break; case 14: $recordType = 'VPN'; App\UsageGeneral::create(['zoneId' => $uuidLookup['zones'][$record->zone_id], 'accountId' => $uuidLookup['accounts'][$record->account_id], 'domainId' => $uuidLookup['domains'][$record->domain_id], 'type' => 'VPN', 'usage' => $record->raw_usage, 'vmInstanceId' => $uuidLookup['instances'][$record->vm_instance_id], 'templateId' => $uuidLookup['templates'][$record->template_id], 'startDate' => $record->start_date, 'endDate' => $record->end_date]); break; default: $recordType = 'Unknown (' . $record->usage_type . ')'; break; } $this->info('Found a record of type ' . $recordType); }); }
/** * Execute the console command. * * @return mixed */ public function fire() { $grandfatherPricing = SiteConfig::whereParameter('grandfatherPricing')->first()->data; $priceMethod = SiteConfig::whereParameter('priceMethod')->first()->data; $domainId = SiteConfig::whereParameter('domainId')->first()->data; $tcOrigin = Config::get('taxcloud.originAddress'); $this->taxCloud->setOriginAddress($tcOrigin['address1'], $tcOrigin['address2'], $tcOrigin['city'], $tcOrigin['state'], $tcOrigin['zip']); if ($priceMethod == 'fixedRatio') { $priceData = SiteConfig::where('parameter', 'LIKE', '%Price')->get(); foreach ($priceData as $pd) { $this->pricing[$pd->parameter] = $pd->data; } } else { if ($priceMethod == 'elementPrice') { $priceData = ElementCost::all(); foreach ($priceData as $pd) { $this->pricing[$pd->element]["{$pd->quantity}-{$pd->quantity_type}"] = $pd->price; } } } // Get all of the users with a bill date of today. $users = User::billableToday()->chunk(100, function ($users) use($grandfatherPricing, $priceMethod, $domainId) { foreach ($users as $user) { $acsAccountData = app('Cloudstack\\CloudStackClient')->listUsers(['account' => $user->email, 'domainid' => $domainId])[0]; $invoiceID = uniqid(); $this->info("Working on user {$user->email}, account ID {$acsAccountData->accountid}"); // Get usage records data for VM, disk and general. UsageInstance::where('accountId', '=', $acsAccountData->accountid)->chunk(100, function ($instanceUsage) { // Go through the usage and make a nice array that we can invoice from foreach ($instanceUsage as $record) { $this->repo->addRecord($record); } }); UsageDisk::where('accountId', '=', $acsAccountData->accountid)->chunk(100, function ($diskUsage) { foreach ($diskUsage as $record) { $this->repo->addRecord($record); } }); // Begin pricing VMs and setting TIC on each record. $usage = $this->repo->all(); print_r($usage); $usage['subtotal'] = 0; // Loop through the aggregated records and add pricing and tax information foreach ($usage['instance'] as $vmid => &$instance) { foreach ($instance as $so_id => &$so_instance) { $pkg = Package::where('cpu_number', '=', $so_instance['resources']['cpunumber'])->where('ram', '=', $so_instance['resources']['memory'])->where('disk_size', '=', $so_instance['resources']['disk_size'])->where('disk_type', '=', $so_instance['resources']['disk_type'])->first(); if ($pkg instanceof Package) { $so_instance['tic'] = $pkg->tic; // Set the price $so_instance['price'] = $pkg->price / 720 * $so_instance['usage']; } else { // Instance has custom parameters and should be priced by its individual elements. if ($priceMethod == 'fixedRatio') { $so_instance['price'] = $so_instance['resources']['cpunumber'] * ($this->pricing['corePrice'] / 720) * $so_instance['usage']; $so_instance['price'] += $so_instance['resources']['memory'] / 1024 * ($this->pricing['ramPrice'] / 720) * $so_instance['usage']; $storageType = DiskType::find($so_instance['resources']['disk_type']); $diskAmount = $so_instance['resources']['disk_size'] / 1024 / 1024 / 1024; $so_instance['price'] += $diskAmount * ($this->pricing["{$storageType->tags}Price"] / 720) * $so_instance['usage']; } else { if ($priceMethod == 'elementPrice') { $so_instance['price'] = $this->pricing['CPU']["{$so_instance['resources']['cpunumber']}"] * $so_instance['usage']; $memoryIdentifier = $so_instance['resources']['memory'] / 1024 . "-GB"; $so_instance['price'] += $this->pricing['RAM']["{$memoryIdentifier}"] * $so_instance['usage']; $storageType = DiskType::find($so_instance['resources']['disk_type']); $diskAmount = $so_instance['resources']['disk_size'] / 1024 / 1024 / 1024; $so_instance['price'] += $this->pricing[$storageType->tags][$diskAmount] * $so_instance['usage']; } } $so_instance['tic'] = 30070; // !!REVISE!! } if ($grandfatherPricing == 'YES') { // Check to see if we have this instance on record $gfInstance = VmInstance::where('vm_instance_id', '=', $vmid)->where('cpu_number', '=', $so_instance['resources']['cpunumber'])->where('memory', '=', $so_instance['resources']['memory'])->where('disk_size', '=', $so_instance['resources']['disk_size'])->where('disk_type', '=', $so_instance['resources']['disk_type'])->first(); if ($gfInstance instanceof VmInstance) { $so_instance['price'] = $gfInstance->rate; } else { $newVm = new VmInstance(['vm_instance_id' => $vmid, 'cpu_number' => $so_instance['resources']['cpunumber'], 'memory' => $so_instance['resources']['memory'], 'disk_size' => $so_instance['resources']['disk_size'], 'disk_type' => $so_instance['resources']['disk_type'], 'rate' => $so_instance['price']]); $user->instances()->save($newVm); } } $usage['subtotal'] += $so_instance['price']; } } // Grab a collection of credit cards. We use billing address off the primary card for tax purposes. $cards = CreditCard::where('user_id', '=', $user->id)->orderBy('primary', 'desc')->get(); $paymentGw = app('\\App\\Repositories\\Contracts\\PaymentRepositoryInterface'); $primaryCard = $paymentGw->get($cards->first()->id, $user->id); // Compute Sales Tax $this->taxCloud->setDestAddress($primaryCard['address'], '', $primaryCard['city'], $primaryCard['state'], strpos($primaryCard['zipcode'], '-') === FALSE ? $primaryCard['zipcode'] : substr($primaryCard['zipcode'], 0, 5), ''); $usage['tax'] = $this->taxCloud->calculateSalesTax($user->id, $invoiceID, $usage); $usage['total'] = $usage['subtotal'] + $usage['tax']; // Record the uniqueID. $usage['invoiceNumber'] = $invoiceID; // Record invoice for the total amount. $transInvoice = new Transaction(['amount' => $usage['total'], 'note' => 'Invoice', 'invoice_number' => $invoiceID]); $user->transactions()->save($transInvoice); // Bill the user foreach ($cards as $card) { $response = $paymentGw->charge($card->payment_profile_id, $usage['total']); if ($response == false) { // Notify the user their card failed to charge. // Go on to the next one. continue; } // Record payment and set next bill date $transPayment = new Transaction(['amount' => -$usage['total'], 'note' => 'Payment']); $user->transactions()->save($transPayment); $user->bill_date = date('Y-m-d', strtotime("+1 month")); break; // If its successful, proceed. } // Serialize the cache and save it for the user it was generated for $invoice = new \App\Invoice(['invoice_data' => serialize($usage)]); $user->invoices()->save($invoice); // Reset the cache for the next user $this->repo->clearCache(); } }); }