Skip to content
This repository has been archived by the owner on Feb 8, 2025. It is now read-only.

feat: add billing #855

Merged
merged 23 commits into from
Jun 30, 2021
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ CURRENCY_LAYER_API_KEY=
UPLOADCARE_PUBLIC_KEY=
UPLOADCARE_PRIVATE_KEY=

# Activate paid plan in the instance
ENABLE_PAID_PLAN=true

# API key for analytics
# We use Fathom (https://usefathom.com) to manage analytics.
# Fathom is not free but is completely privacy friendly and we trust them.
Expand Down
7 changes: 7 additions & 0 deletions app/Console/Kernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
use App\Jobs\StopRateYourManagerProcess;
use App\Jobs\StartRateYourManagerProcess;
use Illuminate\Console\Scheduling\Schedule;
use App\Jobs\Invoicing\CreateMonthlyInvoiceForCompanies;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use App\Jobs\Invoicing\LogDailyMaxNumberOfActiveEmployeesInCompanies;

class Kernel extends ConsoleKernel
{
Expand Down Expand Up @@ -37,6 +39,11 @@ protected function schedule(Schedule $schedule)
$schedule->job(new StartRateYourManagerProcess())->lastDayOfMonth('01:00');
$schedule->job(new StopRateYourManagerProcess())->hourly();

$schedule->command('timeoff:calculate '.Carbon::today()->format('Y-m-d'))->daily();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the same as the commented line below


$schedule->job(new LogDailyMaxNumberOfActiveEmployeesInCompanies())->dailyAt($midnight);
$schedule->job(new CreateMonthlyInvoiceForCompanies())->lastDayOfMonth('22:00');

// disabled until PTOs will be finally implemented
//$schedule->command('timeoff:calculate '.Carbon::today()->format('Y-m-d'))->daily();

Expand Down
54 changes: 54 additions & 0 deletions app/Jobs/Invoicing/CreateMonthlyInvoiceForCompanies.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

namespace App\Jobs\Invoicing;

use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use App\Models\Company\Company;
use App\Models\Company\CompanyInvoice;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use App\Models\Company\CompanyDailyUsageHistory;

class CreateMonthlyInvoiceForCompanies implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

/**
* Create a new job instance.
*/
public function __construct()
{
}

/**
* Create the monthly invoice for the company, based on the usage in the
* account.
*/
public function handle(): void
{
if (! config('officelife.enable_paid_plan')) {
return;
}

Company::chunk(100, function ($companies) {
foreach ($companies as $company) {
$usage = CompanyDailyUsageHistory::where('company_id', $company->id)
->whereBetween('created_at', [Carbon::now()->startOfMonth(), Carbon::now()->endOfMonth()])
->orderBy('number_of_active_employees', 'desc')
->first();

if (! $usage) {
continue;
}

CompanyInvoice::create([
'company_id' => $company->id,
'usage_history_id' => $usage->id,
]);
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

namespace App\Jobs\Invoicing;

use Illuminate\Bus\Queueable;
use App\Models\Company\Company;
use App\Models\Company\Employee;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use App\Models\Company\CompanyDailyUsageHistory;
use App\Models\Company\CompanyUsageHistoryDetails;

class LogDailyMaxNumberOfActiveEmployeesInCompanies implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

/**
* Create a new job instance.
*/
public function __construct()
{
}

/**
* Record the number of active employee for all the companies in the
* instance.
*/
public function handle(): void
{
if (! config('officelife.enable_paid_plan')) {
return;
}

Company::addSelect([
'max_employees' => Employee::selectRaw('count(*)')
->whereColumn('company_id', 'companies.id')
->where('locked', false),
])
->chunk(100, function ($companies) {
foreach ($companies as $company) {
$usage = CompanyDailyUsageHistory::create([
'company_id' => $company->id,
'number_of_active_employees' => $company->max_employees,
]);

Employee::where('company_id', $company->id)
->where('locked', false)
->chunk(100, function ($employees) use ($usage) {
foreach ($employees as $employee) {
CompanyUsageHistoryDetails::create([
'usage_history_id' => $usage->id,
'employee_name' => $employee->name,
'employee_email' => $employee->email,
]);
}
});
}
});
}
}
20 changes: 20 additions & 0 deletions app/Models/Company/Company.php
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,26 @@ public function groups()
return $this->hasMany(Group::class);
}

/**
* Get all company usage history records in the company.
*
* @return HasMany
*/
public function usageHistory()
{
return $this->hasMany(CompanyDailyUsageHistory::class);
}

/**
* Get all company invoice records in the company.
*
* @return HasMany
*/
public function invoices()
{
return $this->hasMany(CompanyInvoice::class);
}

/**
* Get all softwares in the company.
*
Expand Down
47 changes: 47 additions & 0 deletions app/Models/Company/CompanyDailyUsageHistory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

namespace App\Models\Company;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Factories\HasFactory;

class CompanyDailyUsageHistory extends Model
{
use HasFactory;

protected $table = 'company_daily_usage_history';

/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'company_id',
'number_of_active_employees',
'created_at',
];

/**
* Get the Company record associated with the company usage history object.
*
* @return BelongsTo
*/
public function company()
{
return $this->belongsTo(Company::class);
}

/**
* Get the company usage history details records associated with the company
* usage history.
*
* @return HasMany
*/
public function details()
{
return $this->hasMany(CompanyUsageHistoryDetails::class, 'usage_history_id', 'id');
}
}
58 changes: 58 additions & 0 deletions app/Models/Company/CompanyInvoice.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

namespace App\Models\Company;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Factories\HasFactory;

class CompanyInvoice extends Model
{
use HasFactory;

protected $table = 'company_invoices';

/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'company_id',
'usage_history_id',
'sent_to_customer',
'customer_has_paid',
'email_address_invoice_sent_to',
];

/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'sent_to_customer' => 'boolean',
'customer_has_paid' => 'boolean',
];

/**
* Get the Company record associated with the company invoice object.
*
* @return BelongsTo
*/
public function company()
{
return $this->belongsTo(Company::class);
}

/**
* Get the Company usage history record associated with the company invoice
* object.
*
* @return BelongsTo
*/
public function companyUsageHistory()
{
return $this->belongsTo(CompanyDailyUsageHistory::class, 'usage_history_id');
}
}
36 changes: 36 additions & 0 deletions app/Models/Company/CompanyUsageHistoryDetails.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace App\Models\Company;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Factories\HasFactory;

class CompanyUsageHistoryDetails extends Model
{
use HasFactory;

protected $table = 'company_usage_history_details';

/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'usage_history_id',
'employee_name',
'employee_email',
];

/**
* Get the Company usage history record associated with the company
* usage history detail.
*
* @return BelongsTo
*/
public function companyUsageHistory()
{
return $this->belongsTo(CompanyDailyUsageHistory::class, 'usage_history_id');
}
}
11 changes: 11 additions & 0 deletions config/officelife.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,17 @@
*/
'location_iq_url' => env('LOCATION_IQ_URL', 'https://us1.locationiq.com/v1/'),

/*
|--------------------------------------------------------------------------
| Enable payment in the instance
|--------------------------------------------------------------------------
|
| This is used to bill the customers of the OfficeLife instance.
| You most likely don't need to touch this variable if you self-host.
|
*/
'enable_paid_plan' => env('ENABLE_PAID_PLAN', false),

/*
|--------------------------------------------------------------------------
| Mapbox API key
Expand Down
30 changes: 30 additions & 0 deletions database/factories/Company/CompanyDailyUsageHistoryFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace Database\Factories\Company;

use App\Models\Company\Company;
use App\Models\Company\CompanyDailyUsageHistory;
use Illuminate\Database\Eloquent\Factories\Factory;

class CompanyDailyUsageHistoryFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = CompanyDailyUsageHistory::class;

/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
'company_id' => Company::factory(),
'number_of_active_employees' => 3,
];
}
}
Loading