Untitled

mail@pastecode.io avatar
unknown
plain_text
2 years ago
7.3 kB
2
Indexable
Never
<?php

namespace Modules\Billing\Util;

use App\Models\Account;
use App\Models\Instance;
use App\Models\InstanceBackup;
use App\Models\InstanceMarketPlaceAppVersion;
use App\Models\InstanceOsVersion;
use App\Models\InstanceSnapshot;
use App\Models\MarketplaceApp;
use App\Models\OsVersion;
use App\Models\PublicIpv4;
use App\Models\User;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Modules\Billing\Entities\Invoice;
use Modules\Billing\Entities\InvoiceDetail;
use Modules\BlockStorage\Entities\BlockStorage;
use Modules\BlockStorage\Entities\BlockstorageSnapshot;
use Modules\Kubernetes\Entities\KubernetesCluster;
use Modules\LoadBalancer\Entities\LoadBalancer;
use Modules\Partner\Entities\Addon;
use Modules\VPC\Entities\Vpc;

class UsageCalculator
{
    public $billingMethods = ['hourly', 'monthly', 'quarterly', 'semi-annually', 'yearly'];

    public $billingServices = [
        Instance::class,
        BlockStorage::class,
        LoadBalancer::class,
        KubernetesCluster::class,
        InstanceBackup::class,
        InstanceSnapshot::class,
        PublicIpv4::class,
        Vpc::class,
        Addon::class,
        InstanceOsVersion::class,
        InstanceMarketPlaceAppVersion::class,
        BlockstorageSnapshot::class,
    ];

    public function calculateOwn($all = false, $startDate = null, $endDate = null): float
    {
        /** @var Account $account */
        $account = auth()->user()->account;
        return $this->calculateByAccount($account, $startDate, $endDate);
    }

    public function calculateOwnEstimated($startDate = null, $endDate = null): float
    {
        /** @var Account $account */
        $account = auth()->user()->account;
        $hours = now()->diffInHours(now()->endOfMonth());
        return _round($this->getDetailUsageSum($account, $startDate, $endDate, 'usage', 'rate')) * $hours;
    }

    public function calculateTotalConsumption(Account $account, $startDate = null, $endDate = null): float
    {
        return $this->getDetailUsageSum($account, $startDate, $endDate, 'all');
    }

    public function calculateByAccount(Account $account, $startDate = null, $endDate = null): float
    {
        $usage = 0;
        $startDate = Carbon::parse($startDate) ?? now()->startOfMonth();
        $endDate = Carbon::parse($endDate) ?? now()->endOfMonth();

        if (!getSetting('partner_module_enabled')) {
            $usage += $account->invoices()
                ->whereNotNull('paid_at')
                ->whereBetween('paid_at', [$startDate, $endDate])
                ->sum('amount');
        }

        if ($account->customers()->count()) {

            foreach ($account->customers as $account) {
                $usage += $this->getDetailUsageSum($account, $startDate, $endDate);
                $usage += $account->invoices()->whereBetween('start_at', [$startDate, $endDate])->whereType('payable')->sum('amount');
            }
        } else {
            $usage += $this->getDetailUsageSum($account, $startDate, $endDate);
        }

        return $usage;
    }

    private function getDetailUsageSum(Account $account, $startDate, $endDate, $type = 'usage', string $column = 'amount'): float
    {
        $raw = 'ROUND(SUM(ROUND(invoice_details.'.$column.', 2)), 2) as '.$column;
        $sum = Invoice::selectRaw($raw)
                ->join('invoice_details', 'invoices.id', 'invoice_details.invoice_id')
                ->where('invoices.account_id', $account->id)
                ->where(function($q) use($type){
                    if($type != 'all')
                        $q->where('invoices.type', $type);
                })
                ->whereBetween('invoice_details.start_at', [$startDate, $endDate])
                ->first();
        
        return $sum->$column ?? 0;

        $sum = 0;
        $invoiceIds = $account->invoices()->pluck('id')->toArray();
        $details = InvoiceDetail::whereIn('invoice_id', $invoiceIds)->get();
        foreach ($details as $detail) {
            $sum += _round($detail->rate);
        }

        return $sum;

        // $detailSum = $account->invoices()->withSum(['details as detail_amount' => function ($q) use ($startDate, $endDate) {
        //     $q->whereBetween('start_at', [$startDate, $endDate]);
        // }], 'amount')->where(function($q) use ($type){
        //     if($type != 'all')
        //         $q->whereType($type);
        // })->get();

        // return $detailSum->sum('detail_amount');
    }

    public function getSelects($model)
    {
        $version_id = class_basename($model) == 'InstanceOsVersion' ? 'version_id' : 'created_at';
        return 'id, name, project_id, created_at, deleted_at, frozen_at, suspended_at, terminate_at, "' . class_basename($model) . '" as product,' . $version_id;
    }

    public function getService(Account $account, $model, $billingMethod = null, $withTrash = true, $startDate = null, $endDate = null)
    {
        $q = $model::selectRaw((new UsageCalculator())->getSelects($model))
        ->with('offerings', function($q) use($billingMethod, $withTrash){
            if($withTrash) $q->withTrashed();
            if(!empty($billingMethod)) $q->where('billing_period', $billingMethod);
        })
        ->with(['project', 'invoice_details'])
        ->whereIn('project_id', $account->projects()->select('id') )
        ->whereHas('offerings', function($q) use($billingMethod, $withTrash){
            if($withTrash) $q->withTrashed();
            if(!empty($billingMethod)) $q->where('billing_period', $billingMethod);
        })
        ->whereHas('invoice_details')
        ->with('invoices', function($q) use($billingMethod){
            if(!empty($billingMethod) && ($billingMethod) == 'yearly') {
                $q->whereRaw(\DB::raw("DATE(DATE_ADD(invoices.due_at, INTERVAL 1 YEAR)) = DATE(NOW())"));
            }
        })
        ->withSum('offerings', 'total_price')
        ->withSum(['invoice_details' => function($q) use($startDate, $endDate){
            if($startDate && $endDate) $q->whereBetween('created_at', [$startDate, $endDate]);
        }], 'amount');

        if ($withTrash)
            $q->withTrashed();

        return $q->get();
    }

    /**
     * merge all services in one
     */
    public function getServices(Account $account, $billingMethod = null, $withTrash = true, $startDate = null, $endDate = null)
    {
        $default = new Collection();
        foreach ($this->billingServices as $model) {
            $default = $default->merge($this->getService($account, $model, $billingMethod, $withTrash, $startDate, $endDate));
        }
        return $default->sortByDesc('created_at');
    }

    public function currentUsage(Account $account, $service = null, $billingMethod = null)
    {
        if ($billingMethod == 'quarterly') {
            $endAt = now()->endOfQuarter();
        } elseif ($billingMethod == 'semi-annually') {
            $endAt = now()->month < 6 ? now()->month(6)->endOfMonth() : now()->month(12)->endOfMonth();
        } else {
            $endAt = now()->endOfMonth();
        }

        $currentUsage = $service->invoice_details()->whereBetween('created_at', [now()->startOfMonth(), $endAt])->sum('amount') ?? 0;

        return $currentUsage;
    }
}