Page MenuHomePhorge

D1102.1775285355.diff
No OneTemporary

Authored By
Unknown
Size
32 KB
Referenced Files
None
Subscribers
None

D1102.1775285355.diff

diff --git a/bin/quickstart.sh b/bin/quickstart.sh
--- a/bin/quickstart.sh
+++ b/bin/quickstart.sh
@@ -35,7 +35,7 @@
bin/regen-certs
-docker pull kolab/centos7:latest
+#docker pull kolab/centos7:latest
docker-compose down
docker-compose build
diff --git a/docker/redis/Dockerfile b/docker/redis/Dockerfile
--- a/docker/redis/Dockerfile
+++ b/docker/redis/Dockerfile
@@ -1,5 +1,6 @@
FROM fedora:30
+ENV container docker
ENV SYSTEMD_PAGER=''
RUN dnf -y install \
diff --git a/src/.gitignore b/src/.gitignore
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -1,3 +1,4 @@
+*.swp
database/database.sqlite
node_modules/
package-lock.json
diff --git a/src/app/Console/Commands/WalletTransactions.php b/src/app/Console/Commands/WalletTransactions.php
new file mode 100644
--- /dev/null
+++ b/src/app/Console/Commands/WalletTransactions.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace App\Console\Commands;
+
+use Illuminate\Console\Command;
+
+class WalletTransactions extends Command
+{
+ /**
+ * The name and signature of the console command.
+ *
+ * @var string
+ */
+ protected $signature = 'wallet:transactions {--detail} {wallet}';
+
+ /**
+ * The console command description.
+ *
+ * @var string
+ */
+ protected $description = 'List the transactions against a wallet.';
+
+ /**
+ * Create a new command instance.
+ *
+ * @return void
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+ /**
+ * Execute the console command.
+ *
+ * @return mixed
+ */
+ public function handle()
+ {
+ $wallet = \App\Wallet::where('id', $this->argument('wallet'))->first();
+
+ if (!$wallet) {
+ return 1;
+ }
+
+ foreach ($wallet->transactions() as $transaction) {
+ $this->info(
+ sprintf(
+ "%s: %s %s",
+ $transaction->id,
+ $transaction->created_at,
+ $transaction->toString()
+ )
+ );
+
+ if ($this->option('detail')) {
+ $elements = \App\Transaction::where('transaction_id', $transaction->id)
+ ->orderBy('created_at')->get();
+
+ foreach ($elements as $element) {
+ $this->info(
+ sprintf(
+ " + %s: %s %s",
+ $element->id,
+ $element->created_at,
+ $element->toString()
+ )
+ );
+ }
+ }
+ }
+ }
+}
diff --git a/src/app/Entitlement.php b/src/app/Entitlement.php
--- a/src/app/Entitlement.php
+++ b/src/app/Entitlement.php
@@ -52,6 +52,29 @@
'cost' => 'integer',
];
+ /**
+ * Create a transaction record for this entitlement.
+ *
+ * @param int $type The type of transaction ('create', 'bill', 'delete')
+ * @param int $amount The amount involved in cents
+ *
+ * @return string The transaction ID
+ */
+ public function createTransaction($type, $amount = null)
+ {
+ $transaction = \App\Transaction::create(
+ [
+ 'user_email' => \App\Utils::userEmailOrNull(),
+ 'object_id' => $this->id,
+ 'object_type' => \App\Entitlement::class,
+ 'type' => $type,
+ 'amount' => $amount
+ ]
+ );
+
+ return $transaction->id;
+ }
+
/**
* Principally entitleable objects such as 'Domain' or 'User'.
*
diff --git a/src/app/Http/Controllers/API/V4/Admin/WalletsController.php b/src/app/Http/Controllers/API/V4/Admin/WalletsController.php
--- a/src/app/Http/Controllers/API/V4/Admin/WalletsController.php
+++ b/src/app/Http/Controllers/API/V4/Admin/WalletsController.php
@@ -42,7 +42,7 @@
$result['provider'] = $provider->name();
$result['providerLink'] = $provider->customerLink($wallet);
- return $result;
+ return response()->json($result);
}
/**
diff --git a/src/app/Http/Controllers/API/V4/WalletsController.php b/src/app/Http/Controllers/API/V4/WalletsController.php
--- a/src/app/Http/Controllers/API/V4/WalletsController.php
+++ b/src/app/Http/Controllers/API/V4/WalletsController.php
@@ -45,7 +45,7 @@
/**
* Display the specified resource.
*
- * @param int $id
+ * @param string $id
*
* @return \Illuminate\Http\JsonResponse
*/
diff --git a/src/app/Observers/EntitlementObserver.php b/src/app/Observers/EntitlementObserver.php
--- a/src/app/Observers/EntitlementObserver.php
+++ b/src/app/Observers/EntitlementObserver.php
@@ -67,6 +67,8 @@
{
$entitlement->entitleable->updated_at = Carbon::now();
$entitlement->entitleable->save();
+
+ $entitlement->createTransaction(\App\Transaction::ENTITLEMENT_CREATED);
}
/**
@@ -87,5 +89,52 @@
$entitlement->entitleable->updated_at = Carbon::now();
$entitlement->entitleable->save();
+
+ $entitlement->createTransaction(\App\Transaction::ENTITLEMENT_DELETED);
+
+ $cost = 0;
+
+ $discount = $entitlement->wallet->getDiscountRate();
+
+ // anything's free for 14 days
+ if ($entitlement->created_at >= Carbon::now()->subDays(14)) {
+ return;
+ }
+
+ // just in case this had not been billed yet, ever
+ $diffInMonths = $entitlement->updated_at->diffInMonths(Carbon::now());
+
+ $cost += (int) ($entitlement->cost * $discount * $diffInMonths);
+
+ // this moves the hypothetical updated at forward to however many months past the original
+ $updatedAt = $entitlement->updated_at->copy()->addMonthsWithoutOverflow($diffInMonths);
+
+ // now we have the diff in days since the last "billed" period
+ $diffInDays = $updatedAt->diffInDays(Carbon::now());
+
+ $dayOfThisMonth = Carbon::now()->day;
+
+ // days in the month for the month prior to this one.
+ // the price per day is based on the number of days left in the last month
+ $daysInLastMonth = \App\Utils::daysInLastMonth();
+
+ $daysLeftInLastMonth = $daysInLastMonth - $updatedAt->day;
+
+ $pricePerDay = (float)$entitlement->cost / $daysInLastMonth;
+
+ $daysToBill = $daysLeftInLastMonth + $dayOfThisMonth;
+
+ $cost += (int) (round($pricePerDay * $daysToBill, 0));
+
+ if ($cost == 0) {
+ return;
+ }
+
+ $transaction = $entitlement->createTransaction(
+ \App\Transaction::ENTITLEMENT_BILLED,
+ $cost
+ );
+
+ $entitlement->wallet->debit($cost, [$transaction]);
}
}
diff --git a/src/app/Observers/TransactionObserver.php b/src/app/Observers/TransactionObserver.php
new file mode 100644
--- /dev/null
+++ b/src/app/Observers/TransactionObserver.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace App\Observers;
+
+use App\Transaction;
+
+class TransactionObserver
+{
+ /**
+ * Ensure the transaction ID is a custom ID (uuid).
+ *
+ * @param \App\Transaction $transaction The transaction object
+ *
+ * @return void
+ */
+ public function creating(Transaction $transaction): void
+ {
+ while (true) {
+ $allegedly_unique = \App\Utils::uuidStr();
+ if (!Transaction::find($allegedly_unique)) {
+ $transaction->{$transaction->getKeyName()} = $allegedly_unique;
+ break;
+ }
+ }
+ }
+}
diff --git a/src/app/Observers/UserObserver.php b/src/app/Observers/UserObserver.php
--- a/src/app/Observers/UserObserver.php
+++ b/src/app/Observers/UserObserver.php
@@ -108,9 +108,12 @@
// Entitlements do not have referential integrity on the entitled object, so this is our
// way of doing an onDelete('cascade') without the foreign key.
- Entitlement::where('entitleable_id', $user->id)
- ->where('entitleable_type', User::class)
- ->delete();
+ $entitlements = Entitlement::where('entitleable_id', $user->id)
+ ->where('entitleable_type', User::class)->get();
+
+ foreach ($entitlements as $entitlement) {
+ $entitlement->delete();
+ }
// Remove owned users/domains
$wallets = $user->wallets()->pluck('id')->all();
diff --git a/src/app/Providers/AppServiceProvider.php b/src/app/Providers/AppServiceProvider.php
--- a/src/app/Providers/AppServiceProvider.php
+++ b/src/app/Providers/AppServiceProvider.php
@@ -32,6 +32,7 @@
\App\Plan::observe(\App\Observers\PlanObserver::class);
\App\SignupCode::observe(\App\Observers\SignupCodeObserver::class);
\App\Sku::observe(\App\Observers\SkuObserver::class);
+ \App\Transaction::observe(\App\Observers\TransactionObserver::class);
\App\User::observe(\App\Observers\UserObserver::class);
\App\UserAlias::observe(\App\Observers\UserAliasObserver::class);
\App\UserSetting::observe(\App\Observers\UserSettingObserver::class);
diff --git a/src/app/Transaction.php b/src/app/Transaction.php
new file mode 100644
--- /dev/null
+++ b/src/app/Transaction.php
@@ -0,0 +1,203 @@
+<?php
+
+namespace App;
+
+use Illuminate\Database\Eloquent\Model;
+
+class Transaction extends Model
+{
+ protected $fillable = [
+ // actor, if any
+ 'user_email',
+
+ // entitlement, wallet
+ 'object_id',
+ 'object_type',
+
+ // entitlement: created, deleted, billed
+ // wallet: debit, credit, award, penalty
+ 'type',
+
+ 'amount',
+
+ 'description',
+
+ // parent, for example wallet debit is parent for entitlements charged.
+ 'transaction_id'
+ ];
+
+ /** @var array Casts properties as type */
+ protected $casts = [
+ 'amount' => 'integer',
+ ];
+
+ /** @var boolean This model uses an automatically incrementing integer primary key? */
+ public $incrementing = false;
+
+ /** @var string The type of the primary key */
+ protected $keyType = 'string';
+
+ /** @var array Translatable properties */
+ public $translatable = [
+ 'description',
+ ];
+
+ public const ENTITLEMENT_BILLED = 'billed';
+ public const ENTITLEMENT_CREATED = 'created';
+ public const ENTITLEMENT_DELETED = 'deleted';
+
+ public const WALLET_AWARD = 'award';
+ public const WALLET_CREDIT = 'credit';
+ public const WALLET_DEBIT = 'debit';
+ public const WALLET_PENALTY = 'penalty';
+
+ public function entitlement()
+ {
+ if ($this->object_type !== \App\Entitlement::class) {
+ return null;
+ }
+
+ return \App\Entitlement::withTrashed()->where('id', $this->object_id)->first();
+ }
+
+ public function setTypeAttribute($value)
+ {
+ switch ($value) {
+ case self::ENTITLEMENT_BILLED:
+ case self::ENTITLEMENT_CREATED:
+ case self::ENTITLEMENT_DELETED:
+ // TODO: Must be an entitlement.
+ $this->attributes['type'] = $value;
+ break;
+
+ case self::WALLET_AWARD:
+ case self::WALLET_CREDIT:
+ case self::WALLET_DEBIT:
+ case self::WALLET_PENALTY:
+ // TODO: This must be a wallet.
+ $this->attributes['type'] = $value;
+ break;
+
+ default:
+ throw new \Exception("Invalid type value");
+ }
+ }
+
+ public function toArray()
+ {
+ $result = [
+ 'user_email' => $this->user_email,
+ 'entitlement_cost' => $this->getEntitlementCost(),
+ 'object_email' => $this->getEntitlementObjectEmail(),
+ 'sku_title' => $this->getEntitlementSkuTitle(),
+ 'wallet_description' => $this->getWalletDescription(),
+ 'description' => $this->{'description'},
+ 'amount' => $this->amount
+ ];
+
+ return $result;
+ }
+
+ public function toString()
+ {
+ $label = $this->objectTypeToLabelString() . '-' . $this->{'type'};
+
+ return \trans("transactions.{$label}", $this->toArray());
+ }
+
+ public function wallet()
+ {
+ if ($this->object_type !== \App\Wallet::class) {
+ return null;
+ }
+
+ return \App\Wallet::where('id', $this->object_id)->first();
+ }
+
+ /**
+ * Return the costs for this entitlement.
+ *
+ * @return int|null
+ */
+ private function getEntitlementCost(): ?int
+ {
+ if (!$this->entitlement()) {
+ return null;
+ }
+
+ // FIXME: without wallet discount
+ // FIXME: in cents
+ // FIXME: without wallet currency
+ $cost = $this->entitlement()->cost;
+
+ $discount = $this->entitlement()->wallet->getDiscountRate();
+
+ return $cost * $discount;
+ }
+
+ /**
+ * Return the object email if any. This is the email for the target user entitlement.
+ *
+ * @return string|null
+ */
+ private function getEntitlementObjectEmail(): ?string
+ {
+ if (!$this->entitlement()) {
+ return null;
+ }
+
+ return $this->entitlement()->entitleable->email;
+ }
+
+ /**
+ * Return the title for the SKU this entitlement is for.
+ *
+ * @return string|null
+ */
+ private function getEntitlementSkuTitle(): ?string
+ {
+ if (!$this->entitlement()) {
+ return null;
+ }
+
+ return $this->entitlement()->sku->{'title'};
+ }
+
+ /**
+ * Return the description for the wallet, if any, or 'default wallet'.
+ *
+ * @return string
+ */
+ public function getWalletDescription()
+ {
+ if ($this->object_type == \App\Entitlement::class && $this->entitlement()) {
+ $description = $this->entitlement()->wallet->{'description'};
+ return empty($description) ? 'Default wallet' : $description;
+ }
+
+ if ($this->object_type == \App\Wallet::class) {
+ $description = $this->wallet()->{'description'};
+ return empty($description) ? 'Default wallet' : $description;
+ }
+
+ return 'Default wallet';
+ }
+
+ /**
+ * Get a string for use in translation tables derived from the object type.
+ *
+ * @return string|null
+ */
+ private function objectTypeToLabelString(): ?string
+ {
+ if ($this->object_type == \App\Entitlement::class) {
+ return 'entitlement';
+ }
+
+ if ($this->object_type == \App\Wallet::class) {
+ return 'wallet';
+ }
+
+ return null;
+ }
+}
diff --git a/src/app/Utils.php b/src/app/Utils.php
--- a/src/app/Utils.php
+++ b/src/app/Utils.php
@@ -3,6 +3,8 @@
namespace App;
use App\Rules\UserEmailLocal;
+use Carbon\Carbon;
+use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Ramsey\Uuid\Uuid;
@@ -12,6 +14,19 @@
*/
class Utils
{
+ /**
+ * Return the number of days in the month prior to this one.
+ *
+ * @return int
+ */
+ public static function daysInLastMonth()
+ {
+ $start = new Carbon('first day of last month');
+ $end = new Carbon('last day of last month');
+
+ return $start->diffInDays($end);
+ }
+
/**
* Provide all unique combinations of elements in $input, with order and duplicates irrelevant.
*
@@ -30,6 +45,22 @@
return $output;
}
+ /**
+ * Returns the current user's email address or null.
+ *
+ * @return string
+ */
+ public static function userEmailOrNull(): ?string
+ {
+ $user = Auth::user();
+
+ if (!$user) {
+ return null;
+ }
+
+ return $user->email;
+ }
+
/**
* Returns a UUID in the form of an integer.
*
@@ -79,6 +110,27 @@
self::combine($input, $r, $index, $data, $i + 1, $output);
}
+ /**
+ * Create self URL
+ *
+ * @param string $route Route/Path
+ *
+ * @return string Full URL
+ */
+ public static function serviceUrl(string $route): string
+ {
+ $url = \url($route);
+
+ $app_url = trim(\config('app.url'), '/');
+ $pub_url = trim(\config('app.public_url'), '/');
+
+ if ($pub_url != $app_url) {
+ $url = str_replace($app_url, $pub_url, $url);
+ }
+
+ return $url;
+ }
+
/**
* Create a configuration/environment data to be passed to
* the UI
@@ -104,27 +156,6 @@
return $env;
}
- /**
- * Create self URL
- *
- * @param string $route Route/Path
- *
- * @return string Full URL
- */
- public static function serviceUrl(string $route): string
- {
- $url = \url($route);
-
- $app_url = trim(\config('app.url'), '/');
- $pub_url = trim(\config('app.public_url'), '/');
-
- if ($pub_url != $app_url) {
- $url = str_replace($app_url, $pub_url, $url);
- }
-
- return $url;
- }
-
/**
* Email address (login or alias) validation
*
diff --git a/src/app/Wallet.php b/src/app/Wallet.php
--- a/src/app/Wallet.php
+++ b/src/app/Wallet.php
@@ -7,6 +7,7 @@
use Carbon\Carbon;
use Iatstuti\Database\Support\NullableFields;
use Illuminate\Database\Eloquent\Model;
+use Illuminate\Support\Facades\DB;
/**
* The eloquent definition of a wallet -- a container with a chunk of change.
@@ -60,10 +61,19 @@
public function chargeEntitlements($apply = true)
{
$charges = 0;
- $discount = $this->discount ? $this->discount->discount : 0;
- $discount = (100 - $discount) / 100;
+ $discount = $this->getDiscountRate();
+
+ DB::beginTransaction();
+
+ // used to parent individual entitlement billings to the wallet debit.
+ $entitlementTransactions = [];
foreach ($this->entitlements()->get()->fresh() as $entitlement) {
+ // This entitlement is priceless. Literally.
+ if ($entitlement->cost == 0) {
+ continue;
+ }
+
// This entitlement has been created less than or equal to 14 days ago (this is at
// maximum the fourteenth 24-hour period).
if ($entitlement->created_at > Carbon::now()->subDays(14)) {
@@ -91,47 +101,39 @@
$entitlement->updated_at = $entitlement->updated_at->copy()->addMonthsWithoutOverflow($diff);
$entitlement->save();
- // TODO: This would be better done out of the loop (debit() will call save()),
- // but then, maybe we should use a db transaction
- $this->debit($cost);
+ if ($cost == 0) {
+ continue;
+ }
+
+ $entitlementTransactions[] = $entitlement->createTransaction(
+ \App\Transaction::ENTITLEMENT_BILLED,
+ $cost
+ );
}
}
- return $charges;
- }
+ if ($apply) {
+ $this->debit($charges, $entitlementTransactions);
+ }
- /**
- * The discount assigned to the wallet.
- *
- * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
- */
- public function discount()
- {
- return $this->belongsTo('App\Discount', 'discount_id', 'id');
- }
+ DB::commit();
- /**
- * Calculate the expected charges to this wallet.
- *
- * @return int
- */
- public function expectedCharges()
- {
- return $this->chargeEntitlements(false);
+ return $charges;
}
/**
- * Remove a controller from this wallet.
- *
- * @param \App\User $user The user to remove as a controller from this wallet.
+ * Controllers of this wallet.
*
- * @return void
+ * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
- public function removeController(User $user)
+ public function controllers()
{
- if ($this->controllers->contains($user)) {
- $this->controllers()->detach($user);
- }
+ return $this->belongsToMany(
+ 'App\User', // The foreign object definition
+ 'user_accounts', // The table name
+ 'wallet_id', // The local foreign key
+ 'user_id' // The remote foreign key
+ );
}
/**
@@ -147,38 +149,60 @@
$this->save();
+ \App\Transaction::create(
+ [
+ 'user_email' => \App\Utils::userEmailOrNull(),
+ 'object_id' => $this->id,
+ 'object_type' => \App\Wallet::class,
+ 'type' => 'credit',
+ 'amount' => $amount
+ ]
+ );
+
return $this;
}
/**
* Deduct an amount of pecunia from this wallet's balance.
*
- * @param int $amount The amount of pecunia to deduct (in cents).
- *
+ * @param int $amount The amount of pecunia to deduct (in cents).
+ * @param array $eTIDs List of transaction IDs for the individual entitlements that make up
+ * this debit record, if any.
* @return Wallet Self
*/
- public function debit(int $amount): Wallet
+ public function debit(int $amount, array &$eTIDs = []): Wallet
{
+ if ($amount == 0) {
+ return $this;
+ }
+
$this->balance -= $amount;
$this->save();
+ $transaction = \App\Transaction::create(
+ [
+ 'user_email' => \App\Utils::userEmailOrNull(),
+ 'object_id' => $this->id,
+ 'object_type' => \App\Wallet::class,
+ 'type' => 'debit',
+ 'amount' => $amount
+ ]
+ );
+
+ \App\Entitlement::whereIn('id', $eTIDs)->update(['transaction_id', $transaction->id]);
+
return $this;
}
/**
- * Controllers of this wallet.
+ * The discount assigned to the wallet.
*
- * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
+ * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
- public function controllers()
+ public function discount()
{
- return $this->belongsToMany(
- 'App\User', // The foreign object definition
- 'user_accounts', // The table name
- 'wallet_id', // The local foreign key
- 'user_id' // The remote foreign key
- );
+ return $this->belongsTo('App\Discount', 'discount_id', 'id');
}
/**
@@ -191,6 +215,38 @@
return $this->hasMany('App\Entitlement');
}
+ /**
+ * Calculate the expected charges to this wallet.
+ *
+ * @return int
+ */
+ public function expectedCharges()
+ {
+ return $this->chargeEntitlements(false);
+ }
+
+ /**
+ * Return the exact, numeric version of the discount to be applied.
+ *
+ * Ranges from 0 - 100.
+ *
+ * @return int
+ */
+ public function getDiscount()
+ {
+ return $this->discount ? $this->discount->discount : 0;
+ }
+
+ /**
+ * The actual discount rate for use in multiplication
+ *
+ * Ranges from 0.00 to 1.00.
+ */
+ public function getDiscountRate()
+ {
+ return (100 - $this->getDiscount()) / 100;
+ }
+
/**
* The owner of the wallet -- the wallet is in his/her back pocket.
*
@@ -211,6 +267,20 @@
return $this->hasMany('App\Payment');
}
+ /**
+ * Remove a controller from this wallet.
+ *
+ * @param \App\User $user The user to remove as a controller from this wallet.
+ *
+ * @return void
+ */
+ public function removeController(User $user)
+ {
+ if ($this->controllers->contains($user)) {
+ $this->controllers()->detach($user);
+ }
+ }
+
/**
* Any (additional) properties of this wallet.
*
@@ -220,4 +290,19 @@
{
return $this->hasMany('App\WalletSetting');
}
+
+ /**
+ * Retrieve the transactions against this wallet.
+ *
+ * @return iterable \App\Transaction
+ */
+ public function transactions()
+ {
+ return \App\Transaction::where(
+ [
+ 'object_id' => $this->id,
+ 'object_type' => \App\Wallet::class
+ ]
+ )->orderBy('created_at')->get();
+ }
}
diff --git a/src/database/migrations/2014_10_12_000000_create_users_table.php b/src/database/migrations/2014_10_12_000000_create_users_table.php
--- a/src/database/migrations/2014_10_12_000000_create_users_table.php
+++ b/src/database/migrations/2014_10_12_000000_create_users_table.php
@@ -4,6 +4,7 @@
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
+// phpcs:ignore
class CreateUsersTable extends Migration
{
/**
@@ -21,7 +22,6 @@
$table->string('password')->nullable();
$table->string('password_ldap')->nullable();
$table->smallinteger('status');
-// $table->rememberToken();
$table->timestamps();
$table->primary('id');
diff --git a/src/database/migrations/2020_03_16_100000_create_payments.php b/src/database/migrations/2020_03_16_100000_create_payments.php
--- a/src/database/migrations/2020_03_16_100000_create_payments.php
+++ b/src/database/migrations/2020_03_16_100000_create_payments.php
@@ -4,6 +4,7 @@
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
+// phpcs:ignore
class CreatePayments extends Migration
{
/**
diff --git a/src/database/migrations/2020_05_21_080131_create_transactions_table.php b/src/database/migrations/2020_05_21_080131_create_transactions_table.php
new file mode 100644
--- /dev/null
+++ b/src/database/migrations/2020_05_21_080131_create_transactions_table.php
@@ -0,0 +1,44 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+// phpcs:ignore
+class CreateTransactionsTable extends Migration
+{
+ /**
+ * Run the migrations.
+ *
+ * @return void
+ */
+ public function up()
+ {
+ Schema::create(
+ 'transactions',
+ function (Blueprint $table) {
+ $table->string('id', 36)->primary();
+ $table->string('user_email')->nullable();
+ $table->string('object_id', 36)->index();
+ $table->string('object_type', 36)->index();
+ $table->string('type', 8);
+ $table->integer('amount')->nullable();
+ $table->string('description')->nullable();
+ $table->string('transaction_id', 36)->nullable()->index();
+ $table->timestamps();
+
+ $table->index(['object_id', 'object_type']);
+ }
+ );
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('transactions');
+ }
+}
diff --git a/src/package-lock.json b/src/package-lock.json
--- a/src/package-lock.json
+++ b/src/package-lock.json
@@ -833,12 +833,6 @@
"to-fast-properties": "^2.0.0"
}
},
- "@deveodk/vue-toastr": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@deveodk/vue-toastr/-/vue-toastr-1.1.0.tgz",
- "integrity": "sha512-G+VontfAl088m/9MDfJ/+BxDIYmXAHZglMz+VOMTFgp9aPMt88P1b3xlJxA43xsQBot2tmcwAjNVphYIibtFpQ==",
- "dev": true
- },
"@fortawesome/fontawesome-common-types": {
"version": "0.2.27",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.27.tgz",
@@ -1774,9 +1768,9 @@
"dev": true
},
"bootstrap": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.4.1.tgz",
- "integrity": "sha512-tbx5cHubwE6e2ZG7nqM3g/FZ5PQEDMWmMGNrCUBVRPHXTJaH7CBDdsLeu3eCh3B1tzAxTnAbtmrzvWEvT2NNEA==",
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.5.0.tgz",
+ "integrity": "sha512-Z93QoXvodoVslA+PWNdk23Hze4RBYIkpb5h8I2HY2Tu2h7A0LpAgLcyrhrSUyo2/Oxm2l1fRZPs1e5hnxnliXA==",
"dev": true
},
"brace-expansion": {
@@ -6076,9 +6070,9 @@
"dev": true
},
"jquery": {
- "version": "3.4.1",
- "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.4.1.tgz",
- "integrity": "sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw==",
+ "version": "3.5.1",
+ "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz",
+ "integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==",
"dev": true
},
"js-levenshtein": {
@@ -7585,9 +7579,9 @@
}
},
"popper.js": {
- "version": "1.15.0",
- "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.15.0.tgz",
- "integrity": "sha512-w010cY1oCUmI+9KwwlWki+r5jxKfTFDVoadl7MSrIujHU5MJ5OR6HTDj6Xo8aoR/QsA56x8jKjA59qGH4ELtrA==",
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
+ "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==",
"dev": true
},
"portfinder": {
diff --git a/src/resources/lang/en/transactions.php b/src/resources/lang/en/transactions.php
new file mode 100644
--- /dev/null
+++ b/src/resources/lang/en/transactions.php
@@ -0,0 +1,12 @@
+<?php
+
+return [
+ 'entitlement-created' => ':user_email created :sku_title for :object_email',
+ 'entitlement-billed' => ':sku_title for :object_email is billed at :amount',
+ 'entitlement-deleted' => ':user_email deleted :sku_title for :object_email',
+
+ 'wallet-award' => 'Bonus of :amount awarded to :wallet_description; :description',
+ 'wallet-credit' => ':amount was added to the balance of :wallet_description',
+ 'wallet-debit' => ':amount was deducted from the balance of :wallet_description',
+ 'wallet-penalty' => 'The balance of wallet :wallet_description was reduced by :amount; :description'
+];
diff --git a/src/tests/Feature/Controller/PaymentsMollieTest.php b/src/tests/Feature/Controller/PaymentsMollieTest.php
--- a/src/tests/Feature/Controller/PaymentsMollieTest.php
+++ b/src/tests/Feature/Controller/PaymentsMollieTest.php
@@ -9,10 +9,11 @@
use App\WalletSetting;
use GuzzleHttp\Psr7\Response;
use Tests\TestCase;
+use Tests\MollieMocksTrait;
class PaymentsMollieTest extends TestCase
{
- use \Tests\MollieMocksTrait;
+ use MollieMocksTrait;
/**
* {@inheritDoc}
@@ -40,7 +41,7 @@
$john = $this->getTestUser('john@kolab.org');
$wallet = $john->wallets()->first();
$john->setSetting('mollie_id', null);
- Payment::where('wallet_id', $wallet->id)->delete();
+ //Payment::where('wallet_id', $wallet->id)->delete();
Wallet::where('id', $wallet->id)->update(['balance' => 0]);
WalletSetting::where('wallet_id', $wallet->id)->delete();
diff --git a/src/tests/Feature/Controller/PaymentsStripeTest.php b/src/tests/Feature/Controller/PaymentsStripeTest.php
--- a/src/tests/Feature/Controller/PaymentsStripeTest.php
+++ b/src/tests/Feature/Controller/PaymentsStripeTest.php
@@ -9,10 +9,11 @@
use App\WalletSetting;
use GuzzleHttp\Psr7\Response;
use Tests\TestCase;
+use Tests\StripeMocksTrait;
class PaymentsStripeTest extends TestCase
{
- use \Tests\StripeMocksTrait;
+ use StripeMocksTrait;
/**
* {@inheritDoc}
@@ -40,7 +41,7 @@
$john = $this->getTestUser('john@kolab.org');
$wallet = $john->wallets()->first();
$john->setSetting('mollie_id', null);
- Payment::where('wallet_id', $wallet->id)->delete();
+ //Payment::where('wallet_id', $wallet->id)->delete();
Wallet::where('id', $wallet->id)->update(['balance' => 0]);
WalletSetting::where('wallet_id', $wallet->id)->delete();

File Metadata

Mime Type
text/plain
Expires
Sat, Apr 4, 6:49 AM (9 h, 44 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18823416
Default Alt Text
D1102.1775285355.diff (32 KB)

Event Timeline