diff --git a/app/Listeners/BusinessUpdateListener.php b/app/Listeners/BusinessUpdateListener.php index 3ef51cb..f292847 100644 --- a/app/Listeners/BusinessUpdateListener.php +++ b/app/Listeners/BusinessUpdateListener.php @@ -5,12 +5,22 @@ namespace App\Listeners; use App\Models\User; use App\Models\Business; use Illuminate\Support\Arr; +<<<<<<< Updated upstream +use App\Channels\FcmChannel; +use App\Events\BusinessUpdate; +use App\Events\BusinessUserCreate; +use App\Notifications\DBNotification; +use App\Notifications\FcmNotification; +use App\Notifications\MailNotification; +use Illuminate\Queue\InteractsWithQueue; +======= use App\Events\BusinessUpdate; use App\Events\BusinessUserCreate; use App\Notifications\DBNotification; use App\Notifications\MailNotification; use Illuminate\Queue\InteractsWithQueue; +>>>>>>> Stashed changes use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Support\Facades\Notification; @@ -19,6 +29,70 @@ class BusinessUpdateListener public function handle(BusinessUpdate $event) { $payload = $event->message; +<<<<<<< Updated upstream + $business = Business::findOrFail($payload->business)->load('owners'); + + $this->checkWalletIsRunningLow($business); + $this->checkWalletIsEmptyOrNegative($business); + $this->accountHasBeenSuspended($business); + } + + public function checkWalletIsRunningLow(Business $business): void + { + $moving_average_days = 7; + $predict_ahead_hours = 24; + + $hours = $moving_average_days * 24; + $business->load([ + 'cost' => fn ($query) => $query->where('created_at', '>=', now('Asia/Tehran')->subHours($hours)), + ]); + + // The wallet is running out + // Calculate the average consumption of the N past days + $average_cost = $business->cost->sum('cost') / $hours; + + // Average hourly consumption multiplied by the next 24 hours + // If the account does not charge as much as in the next 24 hours, notified. + $message = ['body' => __('notification.business.wallet_almost_empty')]; + if (($business->wallet / $predict_ahead_hours) < $average_cost) { + Notification::send($business->owners, new MailNotification($message)); + Notification::send($business->owners, new DBNotification($message)); + } + } + + public function checkWalletIsEmptyOrNegative(Business $business): void + { + $message = ['body' => __('notification.business.wallet_empty')]; + if ($business->wallet <= 0) { + Notification::send($business->owners, new MailNotification($message)); + Notification::send($business->owners, new DBNotification($message)); + } + } + + public function accountHasBeenSuspended(Business $business): void + { + if ($business->wallet > 0) { + return; + } + + $recent_payments_number = 10; + $negativity_threshold = 20; + $business->load([ + 'transactions' => fn ($query) => $query->where('succeeded', '=', true) + ->orderBy('created_at') + ->take($recent_payments_number), + ]); + + // Your account has been blocked. + // What is the average of the last 10 payments? + $average_payment = $business->transactions->average('amount'); + + $threshold = $average_payment / 100 * $negativity_threshold; + $message = ['body' => __('notification.business.suspended')]; + if ($business->wallet < $threshold) { + Notification::send($business->owners, new MailNotification($message)); + Notification::send($business->owners, new DBNotification($message)); +======= $wallet = $payload?->data?->diff?->wallet; $owners = Business::findOrFail($payload->business)->owners; @@ -27,6 +101,7 @@ class BusinessUpdateListener if ($wallet < 0) { Notification::send($owners, new MailNotification($message)); Notification::send($owners, new DBNotification($message)); +>>>>>>> Stashed changes } } } diff --git a/app/Models/Business.php b/app/Models/Business.php index dc03aef..ad38198 100644 --- a/app/Models/Business.php +++ b/app/Models/Business.php @@ -43,6 +43,7 @@ class Business extends Model implements HasMedia protected $casts = [ 'has_avatar' => 'boolean', 'calculated_at' => 'datetime', + 'wallet' => 'integer', ]; public function getValueOf(?string $key) diff --git a/app/Models/User.php b/app/Models/User.php index 3479edf..1a71790 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -5,6 +5,8 @@ namespace App\Models; use App\Models\File; use App\Models\Model; use App\Models\SoftDeletes; +use App\Models\ReportableRelation; +use Illuminate\Notifications\Notifiable; use Illuminate\Validation\Rule; use Illuminate\Http\UploadedFile; use Spatie\MediaLibrary\HasMedia; diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index cdd86b4..41d59dd 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -45,6 +45,7 @@ class EventServiceProvider extends ServiceProvider ProjectUserCreate::class => [ ProjectUserCreateNotif::class, ], + BusinessUpdate::class => [ BusinessUpdateListener::class, ], diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 927be5d..dc58891 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -38,14 +38,14 @@ class RouteServiceProvider extends ServiceProvider $this->configureRateLimiting(); $this->routes(function () { - Route::prefix('api') + Route::prefix('user/v1') ->middleware('api') ->namespace($this->namespace) ->group(base_path('routes/api.php')); - Route::middleware('web') - ->namespace($this->namespace) - ->group(base_path('routes/web.php')); + // Route::middleware('web') + // ->namespace($this->namespace) + // ->group(base_path('routes/web.php')); }); } diff --git a/bootstrap/app.php b/bootstrap/app.php index fdcb8bd..037e17d 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -52,11 +52,4 @@ $app->singleton( | */ -$app->router->group([ - 'namespace' => 'App\Http\Controllers', - 'prefix' => '/user/v1/' -], function ($router) { - require __DIR__.'/../routes/api.php'; -}); - return $app; diff --git a/composer.lock b/composer.lock index 89d7c77..2d2d322 100644 --- a/composer.lock +++ b/composer.lock @@ -119,16 +119,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.173.23", + "version": "3.173.25", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "259fa1a47a46f81e66b1ea328ed961a58fa061c2" + "reference": "69e4653acf8cd855e9010ec4e0e0a7d074c77ee1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/259fa1a47a46f81e66b1ea328ed961a58fa061c2", - "reference": "259fa1a47a46f81e66b1ea328ed961a58fa061c2", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/69e4653acf8cd855e9010ec4e0e0a7d074c77ee1", + "reference": "69e4653acf8cd855e9010ec4e0e0a7d074c77ee1", "shasum": "" }, "require": { @@ -203,9 +203,9 @@ "support": { "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.173.23" + "source": "https://github.com/aws/aws-sdk-php/tree/3.173.25" }, - "time": "2021-03-05T19:20:35+00:00" + "time": "2021-03-09T19:14:56+00:00" }, { "name": "beberlei/assert", @@ -1313,16 +1313,16 @@ }, { "name": "laravel/framework", - "version": "v8.31.0", + "version": "v8.32.1", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "2aa5c2488d25178ebc097052c7897a0e463ddc35" + "reference": "7c37b64f8153c16b6406f5c28cf37828ebbe8846" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/2aa5c2488d25178ebc097052c7897a0e463ddc35", - "reference": "2aa5c2488d25178ebc097052c7897a0e463ddc35", + "url": "https://api.github.com/repos/laravel/framework/zipball/7c37b64f8153c16b6406f5c28cf37828ebbe8846", + "reference": "7c37b64f8153c16b6406f5c28cf37828ebbe8846", "shasum": "" }, "require": { @@ -1477,7 +1477,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2021-03-04T15:22:36+00:00" + "time": "2021-03-09T15:37:45+00:00" }, { "name": "laravel/helpers", @@ -4094,20 +4094,20 @@ }, { "name": "swiftmailer/swiftmailer", - "version": "v6.2.6", + "version": "v6.2.7", "source": { "type": "git", "url": "https://github.com/swiftmailer/swiftmailer.git", - "reference": "d2791ff0b73247cdc2096b14f5580aba40c12bff" + "reference": "15f7faf8508e04471f666633addacf54c0ab5933" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/d2791ff0b73247cdc2096b14f5580aba40c12bff", - "reference": "d2791ff0b73247cdc2096b14f5580aba40c12bff", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/15f7faf8508e04471f666633addacf54c0ab5933", + "reference": "15f7faf8508e04471f666633addacf54c0ab5933", "shasum": "" }, "require": { - "egulias/email-validator": "^2.0", + "egulias/email-validator": "^2.0|^3.1", "php": ">=7.0.0", "symfony/polyfill-iconv": "^1.0", "symfony/polyfill-intl-idn": "^1.10", @@ -4153,7 +4153,7 @@ ], "support": { "issues": "https://github.com/swiftmailer/swiftmailer/issues", - "source": "https://github.com/swiftmailer/swiftmailer/tree/v6.2.6" + "source": "https://github.com/swiftmailer/swiftmailer/tree/v6.2.7" }, "funding": [ { @@ -4165,7 +4165,7 @@ "type": "tidelift" } ], - "time": "2021-03-05T12:08:49+00:00" + "time": "2021-03-09T12:30:35+00:00" }, { "name": "symfony/console", @@ -6627,30 +6627,35 @@ }, { "name": "webmozart/assert", - "version": "1.9.1", + "version": "1.10.0", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389" + "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", - "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25", + "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0 || ^8.0", + "php": "^7.2 || ^8.0", "symfony/polyfill-ctype": "^1.8" }, "conflict": { "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<3.9.1" + "vimeo/psalm": "<4.6.1 || 4.6.2" }, "require-dev": { - "phpunit/phpunit": "^4.8.36 || ^7.5.13" + "phpunit/phpunit": "^8.5.13" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, "autoload": { "psr-4": { "Webmozart\\Assert\\": "src/" @@ -6674,9 +6679,9 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.9.1" + "source": "https://github.com/webmozarts/assert/tree/1.10.0" }, - "time": "2020-07-08T17:02:28+00:00" + "time": "2021-03-09T10:59:23+00:00" } ], "packages-dev": [ @@ -7183,16 +7188,16 @@ }, { "name": "laravel/sail", - "version": "v1.4.6", + "version": "v1.4.7", "source": { "type": "git", "url": "https://github.com/laravel/sail.git", - "reference": "59ee7e2b2efeb644eabea719186db91d11666733" + "reference": "c57927e1e3919a317034ff706c684f01f15c10d8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sail/zipball/59ee7e2b2efeb644eabea719186db91d11666733", - "reference": "59ee7e2b2efeb644eabea719186db91d11666733", + "url": "https://api.github.com/repos/laravel/sail/zipball/c57927e1e3919a317034ff706c684f01f15c10d8", + "reference": "c57927e1e3919a317034ff706c684f01f15c10d8", "shasum": "" }, "require": { @@ -7239,7 +7244,7 @@ "issues": "https://github.com/laravel/sail/issues", "source": "https://github.com/laravel/sail" }, - "time": "2021-03-03T15:22:44+00:00" + "time": "2021-03-09T19:15:10+00:00" }, { "name": "mockery/mockery", diff --git a/config/app.php b/config/app.php index d517cdc..57ba658 100644 --- a/config/app.php +++ b/config/app.php @@ -65,6 +65,7 @@ return [ App\Providers\EventServiceProvider::class, App\Providers\RouteServiceProvider::class, App\Utilities\Zarinpal\Laravel\ZarinpalServiceProvider::class, + Fruitcake\Cors\CorsServiceProvider::class ], 'aliases' => [ diff --git a/config/cors.php b/config/cors.php index b2930d0..4598a66 100644 --- a/config/cors.php +++ b/config/cors.php @@ -1,27 +1,13 @@ ['api/*', 'sanctum/csrf-cookie'], + 'paths' => ['user/v1/*'], 'allowed_methods' => ['*'], 'allowed_origins' => ['*'], - 'allowed_origins_patterns' => ['Content-Type', 'X-Requested-With'], + 'allowedOriginsPatterns' => ['Content-Type', 'X-Requested-With'], 'allowed_headers' => ['*'], diff --git a/config/cors.php.bcp b/config/cors.php.bcp new file mode 100644 index 0000000..3aca0b8 --- /dev/null +++ b/config/cors.php.bcp @@ -0,0 +1,34 @@ + ['*', 'sanctum/csrf-cookie'], + + 'allowed_methods' => ['*'], + + 'allowed_origins' => ['*'], + + 'allowed_origins_patterns' => ['*'], + + 'allowed_headers' => ['*'], + + 'exposed_headers' => [], + + 'max_age' => 0, + + 'supports_credentials' => false, + +]; diff --git a/config/cors.php.laravel b/config/cors.php.laravel index c3ded3c..756b4ee 100644 --- a/config/cors.php.laravel +++ b/config/cors.php.laravel @@ -1,59 +1,19 @@ ['/*'], - /* - * Matches the request method. `['*']` allows all methods. - */ 'allowed_methods' => ['*'], - /* - * Matches the request origin. `['*']` allows all origins. Wildcards can be used, eg `*.mydomain.com` - */ 'allowed_origins' => ['*'], - /* - * Patterns that can be used with `preg_match` to match the origin. - */ 'allowedOriginsPatterns' => ['Content-Type', 'X-Requested-With'], - /* - * Sets the Access-Control-Allow-Headers response header. `['*']` allows all headers. - */ 'allowed_headers' => ['*'], - /* - * Sets the Access-Control-Expose-Headers response header with these headers. - */ 'exposed_headers' => [], - /* - * Sets the Access-Control-Max-Age response header when > 0. - */ 'max_age' => 0, - /* - * Sets the Access-Control-Allow-Credentials header. - */ 'supports_credentials' => false, ]; diff --git a/config/cors.php.lumen b/config/cors.php.lumen index 8a39e6d..aa2f995 100644 --- a/config/cors.php.lumen +++ b/config/cors.php.lumen @@ -1,20 +1,6 @@ ['api/*', 'sanctum/csrf-cookie'], 'allowed_methods' => ['*'], diff --git a/database/migrations/2020_08_18_085017_fingerprints.php b/database/migrations/2020_08_18_085017_fingerprints.php index 6a72833..d44e60d 100644 --- a/database/migrations/2020_08_18_085017_fingerprints.php +++ b/database/migrations/2020_08_18_085017_fingerprints.php @@ -22,7 +22,7 @@ class Fingerprints extends Migration $table->decimal('latitude', 10, 4); $table->decimal('longitude', 11, 4); $table->char('token', 60)->unique(); - $table->text('fcm_token')->unique()->nullable(); + $table->text('fcm_token')->nullable(); $table->timestamps(); }); } diff --git a/database/seeds/BusinessSeeder.php b/database/seeds/BusinessSeeder.php index 1f39038..35ca1af 100644 --- a/database/seeds/BusinessSeeder.php +++ b/database/seeds/BusinessSeeder.php @@ -72,6 +72,19 @@ class BusinessSeeder extends Seeder Project::insert($projects); DB::table('project_user')->insert($project_user); DB::table('systems')->insert($systems); + + + // make sure user with id 1 is owner in one business + $user_businesses = DB::table('business_user')->where('user_id', 1)->get(); + if ($user_businesses->max('level') < enum('roles.manager.id')){ + $firstRecord = $user_businesses->first(); + DB::table('business_user') + ->where([ + 'business_id' => $firstRecord->business_id, + 'user_id' => $firstRecord->user_id, + 'level' => $firstRecord->level, + ]) ->update(['level' => enum('roles.manager.id')]); + } /** * Every business has one or more owners, which might be the owner of other business as well */ diff --git a/database/seeds/UserSeeder.php b/database/seeds/UserSeeder.php index 92bfaa1..71b231c 100644 --- a/database/seeds/UserSeeder.php +++ b/database/seeds/UserSeeder.php @@ -15,6 +15,6 @@ class UserSeeder extends Seeder Fingerprint::insert( factory(Fingerprint::class, 5000)->raw() ); - User::where('id', 1)->update(['mobile' => '09123456789']); + User::where('id', 1)->update(['email' => 'masouddarvishiv56@gmail.com']); } } diff --git a/docker-compose.yml b/docker-compose.yml index d606ce7..e582e38 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,7 +12,7 @@ services: RABBITMQ_DEFAULT_PASS: "root" RABBITMQ_DEFAULT_VHOST: "/" networks: - - sail + - hi-user volumes: - ./definitions.json:/etc/rabbitmq/definitions.json depends_on: @@ -26,7 +26,7 @@ services: MINIO_ACCESS_KEY: root MINIO_SECRET_KEY: minioroot networks: - - sail + - hi-user depends_on: - laravel.test commander: @@ -42,9 +42,7 @@ services: depends_on: - redis networks: - - sail - depends_on: - - redis + - hi-user redis: image: redis:latest ports: @@ -53,7 +51,7 @@ services: volumes: - redis-data:/data networks: - - sail + - hi-user laravel.test: build: context: ./vendor/laravel/sail/runtimes/8.0 @@ -63,13 +61,15 @@ services: image: sail-8.0/app ports: - '${APP_PORT:-80}:80' + container_name: "liwo-user-app" environment: WWWUSER: '${WWWUSER}' LARAVEL_SAIL: 1 volumes: - '.:/var/www/html' networks: - - sail + - hi-user + - hi depends_on: - mysql mysql: @@ -85,7 +85,7 @@ services: volumes: - 'sailmysql:/var/lib/mysql' networks: - - sail + - hi-user healthcheck: test: ["CMD", "mysqladmin", "ping"] pma: @@ -99,7 +99,7 @@ services: depends_on: - mysql networks: - - sail + - hi-user mailhog: image: mailhog/mailhog logging: @@ -108,10 +108,12 @@ services: - 1025:1025 # smtp server - 8025:8025 # web ui networks: - - sail + - hi-user networks: - sail: - driver: bridge + hi-user: + external: false + hi: + external: true volumes: redis-data: driver: local diff --git a/resources/lang/fa/notification.php b/resources/lang/fa/notification.php index 9e5b113..856ea0d 100644 --- a/resources/lang/fa/notification.php +++ b/resources/lang/fa/notification.php @@ -80,7 +80,9 @@ return [ 'business' => [ 'update' => 'کاربر :user به کسب و کار :business اضافه شد.', - 'update_wallet' => 'پول رو میدی یا پولت کنم', + 'wallet_almost_empty' => 'کیف پول تقریبا خالی است.', + 'wallet_empty' => 'کیف پول خالی است.', + 'suspended' => 'حساب مسدود شد.', ], ];