Page MenuHomePhorge

No OneTemporary

Size
11 KB
Referenced Files
None
Subscribers
None
diff --git a/src/app/Tenant.php b/src/app/Tenant.php
index 6a05c730..197c5daa 100644
--- a/src/app/Tenant.php
+++ b/src/app/Tenant.php
@@ -1,95 +1,94 @@
<?php
namespace App;
use App\Traits\SettingsTrait;
use Illuminate\Database\Eloquent\Model;
/**
* The eloquent definition of a Tenant.
*
* @property int $id
* @property string $title
*/
class Tenant extends Model
{
use SettingsTrait;
protected $fillable = [
'id',
'title',
];
- protected $keyType = 'bigint';
/**
* Utility method to get tenant-specific system setting.
* If the setting is not specified for the tenant a system-wide value will be returned.
*
* @param int $tenantId Tenant identifier
* @param string $key Setting name
*
* @return mixed Setting value
*/
public static function getConfig($tenantId, string $key)
{
// Cache the tenant instance in memory
static $tenant;
if (empty($tenant) || $tenant->id != $tenantId) {
$tenant = null;
if ($tenantId) {
$tenant = self::findOrFail($tenantId);
}
}
// Supported options (TODO: document this somewhere):
// - app.name (tenants.title will be returned)
// - app.public_url and app.url
// - app.support_url
// - mail.from.address and mail.from.name
// - mail.reply_to.address and mail.reply_to.name
// - app.kb.account_delete and app.kb.account_suspended
// - pgp.enable
if ($key == 'app.name') {
return $tenant ? $tenant->title : \config($key);
}
$value = $tenant ? $tenant->getSetting($key) : null;
return $value !== null ? $value : \config($key);
}
/**
* Discounts assigned to this tenant.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function discounts()
{
return $this->hasMany('App\Discount');
}
/**
* SignupInvitations assigned to this tenant.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function signupInvitations()
{
return $this->hasMany('App\SignupInvitation');
}
/*
* Returns the wallet of the tanant (reseller's wallet).
*
* @return ?\App\Wallet A wallet object
*/
public function wallet(): ?Wallet
{
$user = \App\User::where('role', 'reseller')->where('tenant_id', $this->id)->first();
return $user ? $user->wallets->first() : null;
}
}
diff --git a/src/app/Traits/UuidIntKeyTrait.php b/src/app/Traits/UuidIntKeyTrait.php
index 0bf0811d..df6c562d 100644
--- a/src/app/Traits/UuidIntKeyTrait.php
+++ b/src/app/Traits/UuidIntKeyTrait.php
@@ -1,51 +1,41 @@
<?php
namespace App\Traits;
trait UuidIntKeyTrait
{
/**
* Boot function from Laravel.
*/
protected static function bootUuidIntKeyTrait()
{
static::creating(function ($model) {
if (empty($model->{$model->getKeyName()})) {
$allegedly_unique = \App\Utils::uuidInt();
// Verify if unique
if (in_array('Illuminate\Database\Eloquent\SoftDeletes', class_uses($model))) {
while ($model->withTrashed()->find($allegedly_unique)) {
$allegedly_unique = \App\Utils::uuidInt();
}
} else {
while ($model->find($allegedly_unique)) {
$allegedly_unique = \App\Utils::uuidInt();
}
}
$model->{$model->getKeyName()} = $allegedly_unique;
}
});
}
/**
* Get if the key is incrementing.
*
* @return bool
*/
public function getIncrementing()
{
return false;
}
-
- /**
- * Get the key type.
- *
- * @return string
- */
- public function getKeyType()
- {
- return 'bigint';
- }
}
diff --git a/src/resources/js/locale.js b/src/resources/js/locale.js
index afe2b18e..fedff4c9 100644
--- a/src/resources/js/locale.js
+++ b/src/resources/js/locale.js
@@ -1,66 +1,66 @@
import Vue from 'vue'
import VueI18n from 'vue-i18n'
// We do pre-load English localization as this is possible
// the only one that is complete and used as a fallback.
import messages from '../build/js/en.json'
Vue.use(VueI18n)
export const i18n = new VueI18n({
locale: 'en',
fallbackLocale: 'en',
messages: { en: messages },
silentFallbackWarn: true
})
let currentLanguage
const loadedLanguages = ['en'] // our default language that is preloaded
const setI18nLanguage = (lang) => {
i18n.locale = lang
document.querySelector('html').setAttribute('lang', lang)
// Set language for API requests
// Note, it's kinda redundant as we support the cookie
window.axios.defaults.headers.common['Accept-Language'] = lang
// Save the selected language in a cookie, so it can be used server-side
// after page reload. Make the cookie valid for 10 years
const age = 10 * 60 * 60 * 24 * 365
- document.cookie = 'language=' + lang + '; max-age=' + age
+ document.cookie = 'language=' + lang + '; max-age=' + age + '; path=/; secure'
return lang
}
export const getLang = () => {
if (!currentLanguage) {
currentLanguage = document.querySelector('html').getAttribute('lang') || 'en'
}
return currentLanguage
}
export const setLang = lang => {
currentLanguage = lang
loadLangAsync()
}
export function loadLangAsync() {
const lang = getLang()
// If the language was already loaded
if (loadedLanguages.includes(lang)) {
return Promise.resolve(setI18nLanguage(lang))
}
// If the language hasn't been loaded yet
return import(/* webpackChunkName: "locale/[request]" */ `../build/js/${lang}.json`)
.then(messages => {
i18n.setLocaleMessage(lang, messages.default)
loadedLanguages.push(lang)
return setI18nLanguage(lang)
})
}
diff --git a/src/tests/Feature/Console/Wallet/ChargeTest.php b/src/tests/Feature/Console/Wallet/ChargeTest.php
index 628114eb..28991135 100644
--- a/src/tests/Feature/Console/Wallet/ChargeTest.php
+++ b/src/tests/Feature/Console/Wallet/ChargeTest.php
@@ -1,147 +1,147 @@
<?php
namespace Tests\Feature\Console\Wallet;
use Illuminate\Support\Facades\Queue;
use Tests\TestCase;
class ChargeTest extends TestCase
{
/**
* {@inheritDoc}
*/
public function setUp(): void
{
parent::setUp();
$this->deleteTestUser('wallet-charge@kolabnow.com');
}
/**
* {@inheritDoc}
*/
public function tearDown(): void
{
$this->deleteTestUser('wallet-charge@kolabnow.com');
parent::tearDown();
}
/**
* Test command run for a specified wallet
*/
public function testHandleSingle(): void
{
$user = $this->getTestUser('wallet-charge@kolabnow.com');
$wallet = $user->wallets()->first();
$wallet->balance = 0;
$wallet->save();
Queue::fake();
// Non-existing wallet ID
$this->artisan('wallet:charge 123')
->assertExitCode(1)
->expectsOutput("Wallet not found.");
Queue::assertNothingPushed();
// The wallet has no entitlements, expect no charge and no check
$this->artisan('wallet:charge ' . $wallet->id)
->assertExitCode(0);
Queue::assertNothingPushed();
// The wallet has no entitlements, but has negative balance
$wallet->balance = -100;
$wallet->save();
$this->artisan('wallet:charge ' . $wallet->id)
->assertExitCode(0);
Queue::assertPushed(\App\Jobs\WalletCharge::class, 0);
Queue::assertPushed(\App\Jobs\WalletCheck::class, 1);
Queue::assertPushed(\App\Jobs\WalletCheck::class, function ($job) use ($wallet) {
$job_wallet = TestCase::getObjectProperty($job, 'wallet');
return $job_wallet->id === $wallet->id;
});
Queue::fake();
// The wallet has entitlements to charge, and negative balance
- $sku = \App\Sku::where('title', 'mailbox')->first();
+ $sku = \App\Sku::withObjectTenantContext($user)->where('title', 'mailbox')->first();
$entitlement = \App\Entitlement::create([
'wallet_id' => $wallet->id,
'sku_id' => $sku->id,
'cost' => 100,
'entitleable_id' => $user->id,
'entitleable_type' => \App\User::class,
]);
\App\Entitlement::where('id', $entitlement->id)->update([
- 'created_at' => \Carbon\Carbon::now()->subMonths(1),
- 'updated_at' => \Carbon\Carbon::now()->subMonths(1),
+ 'created_at' => \Carbon\Carbon::now()->subMonthsNoOverflow(1),
+ 'updated_at' => \Carbon\Carbon::now()->subMonthsNoOverflow(1),
]);
\App\User::where('id', $user->id)->update([
- 'created_at' => \Carbon\Carbon::now()->subMonths(1),
- 'updated_at' => \Carbon\Carbon::now()->subMonths(1),
+ 'created_at' => \Carbon\Carbon::now()->subMonthsNoOverflow(1),
+ 'updated_at' => \Carbon\Carbon::now()->subMonthsNoOverflow(1),
]);
$this->assertSame(100, $wallet->fresh()->chargeEntitlements(false));
$this->artisan('wallet:charge ' . $wallet->id)
->assertExitCode(0);
Queue::assertPushed(\App\Jobs\WalletCharge::class, 1);
Queue::assertPushed(\App\Jobs\WalletCharge::class, function ($job) use ($wallet) {
$job_wallet = TestCase::getObjectProperty($job, 'wallet');
return $job_wallet->id === $wallet->id;
});
Queue::assertPushed(\App\Jobs\WalletCheck::class, 1);
Queue::assertPushed(\App\Jobs\WalletCheck::class, function ($job) use ($wallet) {
$job_wallet = TestCase::getObjectProperty($job, 'wallet');
return $job_wallet->id === $wallet->id;
});
}
/**
* Test command run for all wallets
*/
public function testHandleAll(): void
{
$user = $this->getTestUser('john@kolab.org');
$wallet = $user->wallets()->first();
$wallet->balance = 0;
$wallet->save();
// backdate john's entitlements and set balance=0 for all wallets
$this->backdateEntitlements($user->entitlements, \Carbon\Carbon::now()->subWeeks(5));
\App\Wallet::where('balance', '<', '0')->update(['balance' => 0]);
$user2 = $this->getTestUser('wallet-charge@kolabnow.com');
$wallet2 = $user2->wallets()->first();
$wallet2->balance = -100;
$wallet2->save();
Queue::fake();
// Non-existing wallet ID
$this->artisan('wallet:charge')->assertExitCode(0);
Queue::assertPushed(\App\Jobs\WalletCheck::class, 2);
Queue::assertPushed(\App\Jobs\WalletCheck::class, function ($job) use ($wallet) {
$job_wallet = TestCase::getObjectProperty($job, 'wallet');
return $job_wallet->id === $wallet->id;
});
Queue::assertPushed(\App\Jobs\WalletCheck::class, function ($job) use ($wallet2) {
$job_wallet = TestCase::getObjectProperty($job, 'wallet');
return $job_wallet->id === $wallet2->id;
});
Queue::assertPushed(\App\Jobs\WalletCharge::class, 1);
Queue::assertPushed(\App\Jobs\WalletCharge::class, function ($job) use ($wallet) {
$job_wallet = TestCase::getObjectProperty($job, 'wallet');
return $job_wallet->id === $wallet->id;
});
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Jan 31, 1:10 AM (2 h, 4 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
426102
Default Alt Text
(11 KB)

Event Timeline