Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117816188
D1102.1775285355.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
32 KB
Referenced Files
None
Subscribers
None
D1102.1775285355.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D1102: An initial implementation of transactions.
Attached
Detach File
Event Timeline