Don't allow cancels from certain states (#396)

* Don't allow cancels from certain states

* Unused imports

* Don't reset the state doubly

* Move SetUserActive into listener; code cleanup

* Unused imports

* Add missing files into htaccess

* Move Command contract to correct folder
This commit is contained in:
Nabeel S 2019-09-16 13:08:26 -04:00 committed by GitHub
parent 41baefbf4a
commit aedb1f22b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
61 changed files with 231 additions and 138 deletions

View File

@ -20,6 +20,7 @@ RedirectMatch 403 ^/storage/.*?$
RedirectMatch 403 ^/tests/.*?$ RedirectMatch 403 ^/tests/.*?$
RedirectMatch 403 ^/vendor/.*?$ RedirectMatch 403 ^/vendor/.*?$
RedirectMatch 403 ^/.bowerrc$ RedirectMatch 403 ^/.bowerrc$
RedirectMatch 403 ^/.env
RedirectMatch 403 ^/artisan$ RedirectMatch 403 ^/artisan$
RedirectMatch 403 ^/composer.json RedirectMatch 403 ^/composer.json
RedirectMatch 403 ^/composer.lock RedirectMatch 403 ^/composer.lock
@ -32,6 +33,7 @@ RedirectMatch 403 ^/package.json
RedirectMatch 403 ^/package-lock.json RedirectMatch 403 ^/package-lock.json
RedirectMatch 403 ^/phpunit.xml RedirectMatch 403 ^/phpunit.xml
RedirectMatch 403 ^/webpack.mix.js RedirectMatch 403 ^/webpack.mix.js
RedirectMatch 403 ^/yarn.lock
# Redirect Trailing Slashes If Not A Folder... # Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-d

View File

@ -2,7 +2,7 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Console\Command; use App\Contracts\Command;
use App\Facades\Utils; use App\Facades\Utils;
use GuzzleHttp\Client; use GuzzleHttp\Client;
use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Collection;

View File

@ -2,7 +2,7 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Console\Command; use App\Contracts\Command;
use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Artisan;
class ComposerCommand extends Command class ComposerCommand extends Command

View File

@ -2,7 +2,7 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Console\Command; use App\Contracts\Command;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Tivie\OS\Detector; use Tivie\OS\Detector;

View File

@ -2,7 +2,7 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Console\Command; use App\Contracts\Command;
use App\Models\Acars; use App\Models\Acars;
use App\Models\Airline; use App\Models\Airline;
use App\Models\Pirep; use App\Models\Pirep;

View File

@ -2,7 +2,7 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Console\Command; use App\Contracts\Command;
use Modules\Installer\Services\ConfigService; use Modules\Installer\Services\ConfigService;
/** /**

View File

@ -2,7 +2,7 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Console\Command; use App\Contracts\Command;
use App\Services\ImportService; use App\Services\ImportService;
class ImportCsv extends Command class ImportCsv extends Command

View File

@ -2,7 +2,7 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Console\Command; use App\Contracts\Command;
class ImportFromClassic extends Command class ImportFromClassic extends Command
{ {

View File

@ -2,7 +2,7 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Console\Command; use App\Contracts\Command;
use App\Models\Enums\NavaidType; use App\Models\Enums\NavaidType;
use App\Models\Navdata; use App\Models\Navdata;

View File

@ -2,7 +2,7 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Console\Command; use App\Contracts\Command;
use GuzzleHttp\Client; use GuzzleHttp\Client;
class TestApi extends Command class TestApi extends Command

View File

@ -2,7 +2,7 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Console\Command; use App\Contracts\Command;
use App\Services\VersionService; use App\Services\VersionService;
use Symfony\Component\Yaml\Yaml; use Symfony\Component\Yaml\Yaml;

View File

@ -2,7 +2,7 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Console\Command; use App\Contracts\Command;
use DB; use DB;
use Symfony\Component\Yaml\Yaml; use Symfony\Component\Yaml\Yaml;

View File

@ -2,7 +2,7 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Console\Command; use App\Contracts\Command;
use App\Services\DatabaseService; use App\Services\DatabaseService;
/** /**

View File

@ -2,7 +2,7 @@
namespace App\Console\Cron; namespace App\Console\Cron;
use App\Console\Command; use App\Contracts\Command;
use App\Events\CronHourly; use App\Events\CronHourly;
/** /**

View File

@ -2,7 +2,7 @@
namespace App\Console\Cron; namespace App\Console\Cron;
use App\Console\Command; use App\Contracts\Command;
use App\Events\CronMonthly; use App\Events\CronMonthly;
/** /**

View File

@ -2,7 +2,7 @@
namespace App\Console\Cron; namespace App\Console\Cron;
use App\Console\Command; use App\Contracts\Command;
use App\Events\CronNightly; use App\Events\CronNightly;
/** /**

View File

@ -2,7 +2,7 @@
namespace App\Console\Cron; namespace App\Console\Cron;
use App\Console\Command; use App\Contracts\Command;
use App\Events\CronWeekly; use App\Events\CronWeekly;
/** /**

View File

@ -1,6 +1,6 @@
<?php <?php
namespace App\Console; namespace App\Contracts;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Symfony\Component\Process\Process; use Symfony\Component\Process\Process;
@ -10,6 +10,14 @@ use Symfony\Component\Process\Process;
*/ */
abstract class Command extends \Illuminate\Console\Command abstract class Command extends \Illuminate\Console\Command
{ {
/**
* @return mixed
*/
abstract public function handle();
/**
* Adjust the logging depending on where we're running from
*/
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
@ -20,11 +28,6 @@ abstract class Command extends \Illuminate\Console\Command
} }
} }
/**
* @return mixed
*/
abstract public function handle();
/** /**
* Splice the logger and replace the active handlers with the handlers from the * Splice the logger and replace the active handlers with the handlers from the
* a stack in config/logging.php * a stack in config/logging.php

View File

@ -7,6 +7,8 @@ namespace App\Contracts;
* *
* @property mixed $id * @property mixed $id
* @property bool $skip_mutator * @property bool $skip_mutator
*
* @method static where(array $array)
*/ */
abstract class Model extends \Illuminate\Database\Eloquent\Model abstract class Model extends \Illuminate\Database\Eloquent\Model
{ {

View File

@ -0,0 +1,36 @@
<?php
use App\Models\Enums\PirepState;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
class PirepsChangeStateType extends Migration
{
/**
* Change the PIREP state column to be a TINYINT
*
* @return void
*/
public function up()
{
// Migrate the old rejected state
DB::update('UPDATE `pireps` SET `state`='.PirepState::REJECTED
.' WHERE state=-1');
// Change the column type to an unsigned small int (tinyint not supported on all)
Schema::table('pireps', function (Blueprint $table) {
$table->unsignedSmallInteger('state')->change();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace App\Exceptions;
use App\Models\Pirep;
class PirepCancelNotAllowed extends HttpException
{
private $pirep;
public function __construct(Pirep $pirep)
{
$this->pirep = $pirep;
parent::__construct(
400,
'This PIREP can\'t be cancelled'
);
}
/**
* Return the RFC 7807 error type (without the URL root)
*/
public function getErrorType(): string
{
return 'pirep-cancel-not-allowed';
}
public function getErrorDetails(): string
{
return $this->getMessage();
}
public function getErrorMetadata(): array
{
return [
'pirep_id' => $this->pirep->id,
'state' => $this->pirep->state,
];
}
}

View File

@ -363,10 +363,8 @@ class PirepController extends Controller
{ {
Log::info('PIREP Cancel, user '.Auth::id(), $request->post()); Log::info('PIREP Cancel, user '.Auth::id(), $request->post());
$pirep = $this->pirepRepo->update([ $pirep = Pirep::find($pirep_id);
'state' => PirepState::CANCELLED, $this->pirepSvc->cancel($pirep);
'status' => PirepStatus::CANCELLED,
], $pirep_id);
return new PirepResource($pirep); return new PirepResource($pirep);
} }

View File

@ -46,6 +46,7 @@ Route::group(['middleware' => ['api.auth']], function () {
Route::post('pireps/{pirep_id}/update', 'PirepController@update'); Route::post('pireps/{pirep_id}/update', 'PirepController@update');
Route::post('pireps/{pirep_id}/file', 'PirepController@file'); Route::post('pireps/{pirep_id}/file', 'PirepController@file');
Route::post('pireps/{pirep_id}/comments', 'PirepController@comments_post'); Route::post('pireps/{pirep_id}/comments', 'PirepController@comments_post');
Route::put('pireps/{pirep_id}/cancel', 'PirepController@cancel');
Route::delete('pireps/{pirep_id}/cancel', 'PirepController@cancel'); Route::delete('pireps/{pirep_id}/cancel', 'PirepController@cancel');
Route::get('pireps/{pirep_id}/fields', 'PirepController@fields_get'); Route::get('pireps/{pirep_id}/fields', 'PirepController@fields_get');

View File

@ -0,0 +1,23 @@
<?php
namespace App\Listeners;
use App\Contracts\Listener;
use App\Events\PirepFiled;
use App\Events\UserStateChanged;
use App\Models\Enums\UserState;
class SetUserActive extends Listener
{
public function handle(PirepFiled $event): void
{
// Check the user state, set them to ACTIVE if on leave
if ($event->pirep->user->state !== UserState::ACTIVE) {
$old_state = $event->pirep->user->state;
$event->pirep->user->state = UserState::ACTIVE;
$event->pirep->user->save();
event(new UserStateChanged($event->pirep->user, $old_state));
}
}
}

View File

@ -10,14 +10,17 @@ use App\Models\Traits\FilesTrait;
/** /**
* @property int id * @property int id
* @property mixed subfleet_id * @property mixed subfleet_id
* @property string airport_id The apt where the aircraft is
* @property string name * @property string name
* @property string icao * @property string icao
* @property string registration * @property string registration
* @property int flight_time
* @property string hex_code * @property string hex_code
* @property Airport airport * @property Airport airport
* @property Subfleet subfleet * @property Subfleet subfleet
* @property int status * @property int status
* @property int state * @property int state
* @property Carbon landing_time
*/ */
class Aircraft extends Model class Aircraft extends Model
{ {

View File

@ -4,9 +4,6 @@ namespace App\Models\Enums;
use App\Contracts\Enum; use App\Contracts\Enum;
/**
* Class AcarsType
*/
class AcarsType extends Enum class AcarsType extends Enum
{ {
public const FLIGHT_PATH = 0; public const FLIGHT_PATH = 0;

View File

@ -4,9 +4,6 @@ namespace App\Models\Enums;
use App\Contracts\Enum; use App\Contracts\Enum;
/**
* Class ActiveState
*/
class ActiveState extends Enum class ActiveState extends Enum
{ {
public const INACTIVE = 0; public const INACTIVE = 0;

View File

@ -4,9 +4,6 @@ namespace App\Models\Enums;
use App\Contracts\Enum; use App\Contracts\Enum;
/**
* Class AircraftState
*/
class AircraftState extends Enum class AircraftState extends Enum
{ {
public const PARKED = 0; public const PARKED = 0;

View File

@ -4,9 +4,6 @@ namespace App\Models\Enums;
use App\Contracts\Enum; use App\Contracts\Enum;
/**
* Class AircraftState
*/
class AircraftStatus extends Enum class AircraftStatus extends Enum
{ {
public const ACTIVE = 'A'; public const ACTIVE = 'A';

View File

@ -4,9 +4,6 @@ namespace App\Models\Enums;
use App\Contracts\Enum; use App\Contracts\Enum;
/**
* Class AnalyticsDimensions
*/
class AnalyticsDimensions extends Enum class AnalyticsDimensions extends Enum
{ {
public const PHP_VERSION = 1; public const PHP_VERSION = 1;

View File

@ -5,7 +5,6 @@ namespace App\Models\Enums;
use App\Contracts\Enum; use App\Contracts\Enum;
/** /**
* Class AnalyticsMetrics
* Metrics IDs used in Google Analytics * Metrics IDs used in Google Analytics
*/ */
class AnalyticsMetrics extends Enum class AnalyticsMetrics extends Enum

View File

@ -5,7 +5,6 @@ namespace App\Models\Enums;
use App\Contracts\Enum; use App\Contracts\Enum;
/** /**
* Class Days
* Start on Monday - ISO8601 * Start on Monday - ISO8601
*/ */
class Days extends Enum class Days extends Enum

View File

@ -4,9 +4,6 @@ namespace App\Models\Enums;
use App\Contracts\Enum; use App\Contracts\Enum;
/**
* Class ExpenseType
*/
class ExpenseType extends Enum class ExpenseType extends Enum
{ {
public const FLIGHT = 'F'; public const FLIGHT = 'F';

View File

@ -4,9 +4,6 @@ namespace App\Models\Enums;
use App\Contracts\Enum; use App\Contracts\Enum;
/**
* Class FlightType
*/
class FlightType extends Enum class FlightType extends Enum
{ {
public const SCHED_PAX = 'J'; public const SCHED_PAX = 'J';

View File

@ -4,9 +4,6 @@ namespace App\Models\Enums;
use App\Contracts\Enum; use App\Contracts\Enum;
/**
* Class FuelType
*/
class FuelType extends Enum class FuelType extends Enum
{ {
public const LOW_LEAD = 0; public const LOW_LEAD = 0;

View File

@ -4,9 +4,6 @@ namespace App\Models\Enums;
use App\Contracts\Enum; use App\Contracts\Enum;
/**
* Class JournalType
*/
class JournalType extends Enum class JournalType extends Enum
{ {
public const AIRLINE = 0; public const AIRLINE = 0;

View File

@ -4,9 +4,6 @@ namespace App\Models\Enums;
use App\Contracts\Enum; use App\Contracts\Enum;
/**
* Class AcarsType
*/
class PirepFieldSource extends Enum class PirepFieldSource extends Enum
{ {
public const MANUAL = 0; public const MANUAL = 0;

View File

@ -4,9 +4,6 @@ namespace App\Models\Enums;
use App\Contracts\Enum; use App\Contracts\Enum;
/**
* Class PirepSource
*/
class PirepSource extends Enum class PirepSource extends Enum
{ {
public const MANUAL = 0; public const MANUAL = 0;

View File

@ -9,21 +9,21 @@ use App\Contracts\Enum;
*/ */
class PirepState extends Enum class PirepState extends Enum
{ {
public const REJECTED = -1;
public const IN_PROGRESS = 0; // flight is ongoing public const IN_PROGRESS = 0; // flight is ongoing
public const PENDING = 1; // waiting admin approval public const PENDING = 1; // waiting admin approval
public const ACCEPTED = 2; public const ACCEPTED = 2;
public const CANCELLED = 3; public const CANCELLED = 3;
public const DELETED = 4; public const DELETED = 4;
public const DRAFT = 5; public const DRAFT = 5;
public const REJECTED = 6;
protected static $labels = [ protected static $labels = [
self::REJECTED => 'pireps.state.rejected',
self::IN_PROGRESS => 'pireps.state.in_progress', self::IN_PROGRESS => 'pireps.state.in_progress',
self::PENDING => 'pireps.state.pending', self::PENDING => 'pireps.state.pending',
self::ACCEPTED => 'pireps.state.accepted', self::ACCEPTED => 'pireps.state.accepted',
self::CANCELLED => 'pireps.state.cancelled', self::CANCELLED => 'pireps.state.cancelled',
self::DELETED => 'pireps.state.deleted', self::DELETED => 'pireps.state.deleted',
self::DRAFT => 'pireps.state.draft', self::DRAFT => 'pireps.state.draft',
self::REJECTED => 'pireps.state.rejected',
]; ];
} }

View File

@ -4,9 +4,6 @@ namespace App\Models\Enums;
use App\Contracts\Enum; use App\Contracts\Enum;
/**
* Class UserState
*/
class UserState extends Enum class UserState extends Enum
{ {
public const PENDING = 0; public const PENDING = 0;

View File

@ -42,6 +42,7 @@ use Illuminate\Support\Collection;
* @property Collection fields * @property Collection fields
* @property int status * @property int status
* @property bool state * @property bool state
* @property string source
* @property Carbon submitted_at * @property Carbon submitted_at
* @property Carbon created_at * @property Carbon created_at
* @property Carbon updated_at * @property Carbon updated_at
@ -130,7 +131,7 @@ class Pirep extends Model
'route' => 'nullable', 'route' => 'nullable',
]; ];
/** /*
* If a PIREP is in these states, then it can't be changed. * If a PIREP is in these states, then it can't be changed.
*/ */
public static $read_only_states = [ public static $read_only_states = [
@ -139,6 +140,16 @@ class Pirep extends Model
PirepState::CANCELLED, PirepState::CANCELLED,
]; ];
/*
* If a PIREP is in one of these states, it can't be cancelled
*/
public static $cancel_states = [
PirepState::ACCEPTED,
PirepState::REJECTED,
PirepState::CANCELLED,
PirepState::DELETED,
];
/** /**
* Get the flight ident, e.,g JBU1900 * Get the flight ident, e.,g JBU1900
* *

View File

@ -11,6 +11,8 @@ use App\Models\Enums\PirepFieldSource;
* @property string slug * @property string slug
* @property string value * @property string value
* @property string source * @property string source
*
* @method static updateOrCreate(array $array, array $array1)
*/ */
class PirepFieldValue extends Model class PirepFieldValue extends Model
{ {

View File

@ -10,6 +10,8 @@ use App\Contracts\Model;
* @property float manual_base_pay_rate * @property float manual_base_pay_rate
* @property float acars_base_pay_rate * @property float acars_base_pay_rate
* @property bool auto_promote * @property bool auto_promote
* @property bool auto_approve_acars
* @property bool auto_approve_manual
*/ */
class Rank extends Model class Rank extends Model
{ {

View File

@ -32,6 +32,7 @@ use Laratrust\Traits\LaratrustUserTrait;
* @property int rank_id * @property int rank_id
* @property int state * @property int state
* @property bool opt_in * @property bool opt_in
* @property string last_pirep_id
* @mixin \Illuminate\Notifications\Notifiable * @mixin \Illuminate\Notifications\Notifiable
* @mixin \Laratrust\Traits\LaratrustUserTrait * @mixin \Laratrust\Traits\LaratrustUserTrait
*/ */

View File

@ -3,12 +3,14 @@
namespace App\Providers; namespace App\Providers;
use App\Events\Expenses; use App\Events\Expenses;
use App\Events\PirepFiled;
use App\Events\UserStatsChanged; use App\Events\UserStatsChanged;
use App\Listeners\AwardListener; use App\Listeners\AwardListener;
use App\Listeners\BidEvents; use App\Listeners\BidEvents;
use App\Listeners\ExpenseListener; use App\Listeners\ExpenseListener;
use App\Listeners\FinanceEvents; use App\Listeners\FinanceEvents;
use App\Listeners\NotificationEvents; use App\Listeners\NotificationEvents;
use App\Listeners\SetUserActive;
use Illuminate\Auth\Events\Registered; use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification; use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
@ -20,6 +22,10 @@ class EventServiceProvider extends ServiceProvider
ExpenseListener::class, ExpenseListener::class,
], ],
PirepFiled::class => [
SetUserActive::class,
],
Registered::class => [ Registered::class => [
SendEmailVerificationNotification::class, SendEmailVerificationNotification::class,
], ],

View File

@ -15,9 +15,6 @@ use League\Geotools\Geotools;
use PhpUnitsOfMeasure\Exception\NonNumericValue; use PhpUnitsOfMeasure\Exception\NonNumericValue;
use PhpUnitsOfMeasure\Exception\NonStringUnitName; use PhpUnitsOfMeasure\Exception\NonStringUnitName;
/**
* Class AnalyticsService
*/
class AirportService extends Service class AirportService extends Service
{ {
private $airportRepo; private $airportRepo;

View File

@ -4,14 +4,11 @@ namespace App\Services;
use App\Contracts\Service; use App\Contracts\Service;
use App\Models\Enums\AnalyticsDimensions; use App\Models\Enums\AnalyticsDimensions;
use DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Irazasyed\LaravelGAMP\Facades\GAMP; use Irazasyed\LaravelGAMP\Facades\GAMP;
use Log;
use PDO; use PDO;
/**
* Class AnalyticsService
*/
class AnalyticsService extends Service class AnalyticsService extends Service
{ {
/** /**

View File

@ -4,11 +4,8 @@ namespace App\Services;
use App\Contracts\Service; use App\Contracts\Service;
use App\Support\ClassLoader; use App\Support\ClassLoader;
use Module; use Nwidart\Modules\Facades\Module;
/**
* Class AwardService
*/
class AwardService extends Service class AwardService extends Service
{ {
/** /**

View File

@ -7,9 +7,6 @@ use App\Support\Database;
use Carbon\Carbon; use Carbon\Carbon;
use Webpatser\Uuid\Uuid; use Webpatser\Uuid\Uuid;
/**
* Class DatabaseService
*/
class DatabaseService extends Service class DatabaseService extends Service
{ {
protected $time_fields = [ protected $time_fields = [

View File

@ -11,14 +11,11 @@ use App\Services\ImportExport\FareExporter;
use App\Services\ImportExport\FlightExporter; use App\Services\ImportExport\FlightExporter;
use App\Services\ImportExport\SubfleetExporter; use App\Services\ImportExport\SubfleetExporter;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use League\Csv\CharsetConverter; use League\Csv\CharsetConverter;
use League\Csv\Writer; use League\Csv\Writer;
use Log;
/**
* Class ExportService
*/
class ExportService extends Service class ExportService extends Service
{ {
/** /**

View File

@ -11,9 +11,6 @@ use App\Models\Subfleet;
use App\Support\Math; use App\Support\Math;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
/**
* Class FareService
*/
class FareService extends Service class FareService extends Service
{ {
/** /**

View File

@ -15,9 +15,6 @@ use App\Repositories\NavdataRepository;
use App\Support\Units\Time; use App\Support\Units\Time;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
/**
* Class FlightService
*/
class FlightService extends Service class FlightService extends Service
{ {
private $airportSvc; private $airportSvc;

View File

@ -16,9 +16,6 @@ use Illuminate\Support\Facades\Log;
use League\Geotools\Coordinate\Coordinate; use League\Geotools\Coordinate\Coordinate;
use League\Geotools\Geotools; use League\Geotools\Geotools;
/**
* Class GeoService
*/
class GeoService extends Service class GeoService extends Service
{ {
private $acarsRepo; private $acarsRepo;

View File

@ -19,9 +19,6 @@ use Illuminate\Validation\ValidationException;
use League\Csv\Exception; use League\Csv\Exception;
use League\Csv\Reader; use League\Csv\Reader;
/**
* Class ImportService
*/
class ImportService extends Service class ImportService extends Service
{ {
protected $flightRepo; protected $flightRepo;

View File

@ -4,9 +4,6 @@ namespace App\Services;
use App\Contracts\Service; use App\Contracts\Service;
/**
* Class ModuleService
*/
class ModuleService extends Service class ModuleService extends Service
{ {
protected static $adminLinks = []; protected static $adminLinks = [];

View File

@ -6,20 +6,19 @@ use App\Contracts\Service;
use App\Events\PirepAccepted; use App\Events\PirepAccepted;
use App\Events\PirepFiled; use App\Events\PirepFiled;
use App\Events\PirepRejected; use App\Events\PirepRejected;
use App\Events\UserStateChanged;
use App\Events\UserStatsChanged; use App\Events\UserStatsChanged;
use App\Exceptions\PirepCancelNotAllowed;
use App\Models\Acars; use App\Models\Acars;
use App\Models\Aircraft;
use App\Models\Bid; use App\Models\Bid;
use App\Models\Enums\AcarsType; use App\Models\Enums\AcarsType;
use App\Models\Enums\PirepSource; use App\Models\Enums\PirepSource;
use App\Models\Enums\PirepState; use App\Models\Enums\PirepState;
use App\Models\Enums\PirepStatus; use App\Models\Enums\PirepStatus;
use App\Models\Enums\UserState;
use App\Models\Navdata; use App\Models\Navdata;
use App\Models\Pirep; use App\Models\Pirep;
use App\Models\PirepFieldValue; use App\Models\PirepFieldValue;
use App\Models\User; use App\Models\User;
use App\Repositories\PirepRepository;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
@ -31,19 +30,23 @@ class PirepService extends Service
{ {
private $geoSvc; private $geoSvc;
private $pilotSvc; private $pilotSvc;
private $pirepRepo;
/** /**
* PirepService constructor. * PirepService constructor.
* *
* @param GeoService $geoSvc * @param GeoService $geoSvc
* @param PirepRepository $pirepRepo
* @param UserService $pilotSvc * @param UserService $pilotSvc
*/ */
public function __construct( public function __construct(
GeoService $geoSvc, GeoService $geoSvc,
PirepRepository $pirepRepo,
UserService $pilotSvc UserService $pilotSvc
) { ) {
$this->geoSvc = $geoSvc; $this->geoSvc = $geoSvc;
$this->pilotSvc = $pilotSvc; $this->pilotSvc = $pilotSvc;
$this->pirepRepo = $pirepRepo;
} }
/** /**
@ -207,11 +210,11 @@ class PirepService extends Service
// behavior from the rank that the pilot is assigned to // behavior from the rank that the pilot is assigned to
$default_state = PirepState::PENDING; $default_state = PirepState::PENDING;
if ($pirep->source === PirepSource::ACARS) { if ($pirep->source === PirepSource::ACARS) {
if ($pirep->pilot->rank->auto_approve_acars) { if ($pirep->user->rank->auto_approve_acars) {
$default_state = PirepState::ACCEPTED; $default_state = PirepState::ACCEPTED;
} }
} else { } else {
if ($pirep->pilot->rank->auto_approve_manual) { if ($pirep->user->rank->auto_approve_manual) {
$default_state = PirepState::ACCEPTED; $default_state = PirepState::ACCEPTED;
} }
} }
@ -222,21 +225,36 @@ class PirepService extends Service
// only update the pilot last state if they are accepted // only update the pilot last state if they are accepted
if ($default_state === PirepState::ACCEPTED) { if ($default_state === PirepState::ACCEPTED) {
$pirep = $this->accept($pirep); $pirep = $this->accept($pirep);
$this->setPilotState($pirep->pilot, $pirep);
} else { } else {
$pirep->state = $default_state; $pirep->state = $default_state;
} }
$pirep->save(); $pirep->save();
// Check the user state, set them to ACTIVE if on leave
if ($pirep->user->state !== UserState::ACTIVE) {
$old_state = $pirep->user->state;
$pirep->user->state = UserState::ACTIVE;
$pirep->user->save();
event(new UserStateChanged($pirep->user, $old_state));
} }
/**
* Cancel a PIREP
*
* @param Pirep $pirep
*
* @throws \Prettus\Validator\Exceptions\ValidatorException
*
* @return Pirep
*/
public function cancel(Pirep $pirep): Pirep
{
if (in_array($pirep->state, Pirep::$cancel_states, true)) {
Log::info('PIREP '.$pirep->id.' can\'t be cancelled, state='.$pirep->state);
throw new PirepCancelNotAllowed($pirep);
}
$pirep = $this->pirepRepo->update([
'state' => PirepState::CANCELLED,
'status' => PirepStatus::CANCELLED,
], $pirep->id);
return $pirep;
} }
/** /**
@ -324,12 +342,12 @@ class PirepService extends Service
} }
$ft = $pirep->flight_time; $ft = $pirep->flight_time;
$pilot = $pirep->pilot; $pilot = $pirep->user;
$this->pilotSvc->adjustFlightTime($pilot, $ft); $this->pilotSvc->adjustFlightTime($pilot, $ft);
$this->pilotSvc->adjustFlightCount($pilot, +1); $this->pilotSvc->adjustFlightCount($pilot, +1);
$this->pilotSvc->calculatePilotRank($pilot); $this->pilotSvc->calculatePilotRank($pilot);
$pirep->pilot->refresh(); $pirep->user->refresh();
// Change the status // Change the status
$pirep->state = PirepState::ACCEPTED; $pirep->state = PirepState::ACCEPTED;
@ -362,13 +380,13 @@ class PirepService extends Service
// If this was previously ACCEPTED, then reconcile the flight hours // If this was previously ACCEPTED, then reconcile the flight hours
// that have already been counted, etc // that have already been counted, etc
if ($pirep->state === PirepState::ACCEPTED) { if ($pirep->state === PirepState::ACCEPTED) {
$pilot = $pirep->pilot; $user = $pirep->user;
$ft = $pirep->flight_time * -1; $ft = $pirep->flight_time * -1;
$this->pilotSvc->adjustFlightTime($pilot, $ft); $this->pilotSvc->adjustFlightTime($user, $ft);
$this->pilotSvc->adjustFlightCount($pilot, -1); $this->pilotSvc->adjustFlightCount($user, -1);
$this->pilotSvc->calculatePilotRank($pilot); $this->pilotSvc->calculatePilotRank($user);
$pirep->pilot->refresh(); $pirep->user->refresh();
} }
// Change the status // Change the status
@ -387,6 +405,7 @@ class PirepService extends Service
} }
/** /**
* @param User $pilot
* @param Pirep $pirep * @param Pirep $pirep
*/ */
public function setPilotState(User $pilot, Pirep $pirep) public function setPilotState(User $pilot, Pirep $pirep)

View File

@ -21,9 +21,6 @@ use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use function is_array; use function is_array;
/**
* Class UserService
*/
class UserService extends Service class UserService extends Service
{ {
private $aircraftRepo; private $aircraftRepo;

View File

@ -2,6 +2,7 @@
use App\Models\Enums\PirepState; use App\Models\Enums\PirepState;
use App\Models\Enums\PirepStatus; use App\Models\Enums\PirepStatus;
use App\Models\PirepFare;
use App\Repositories\SettingRepository; use App\Repositories\SettingRepository;
/** /**
@ -217,7 +218,7 @@ class AcarsTest extends TestCase
$pirep = $response->json('data'); $pirep = $response->json('data');
// See that the fields and fares were set // See that the fields and fares were set
$fares = \App\Models\PirepFare::where('pirep_id', $pirep['id'])->get(); $fares = PirepFare::where('pirep_id', $pirep['id'])->get();
$this->assertCount(1, $fares); $this->assertCount(1, $fares);
$saved_fare = $fares->first(); $saved_fare = $fares->first();
@ -246,16 +247,30 @@ class AcarsTest extends TestCase
]; ];
$response = $this->post($uri, $update); $response = $this->post($uri, $update);
$response->assertStatus(200); $response->assertOk();
$updated_pirep = $response->json('data');
// Make sure there are no duplicates // Make sure there are no duplicates
$fares = \App\Models\PirepFare::where('pirep_id', $pirep['id'])->get(); $fares = PirepFare::where('pirep_id', $pirep['id'])->get();
$this->assertCount(1, $fares); $this->assertCount(1, $fares);
$saved_fare = $fares->first(); $saved_fare = $fares->first();
$this->assertEquals($fare->id, $saved_fare['fare_id']); $this->assertEquals($fare->id, $saved_fare['fare_id']);
$this->assertEquals($fare->capacity, $saved_fare['count']); $this->assertEquals($fare->capacity, $saved_fare['count']);
/*
* Try cancelling the PIREP now
*/
$uri = '/api/pireps/'.$pirep['id'].'/cancel';
$response = $this->put($uri, []);
$response->assertOk();
// Read it
$uri = '/api/pireps/'.$pirep['id'];
$response = $this->get($uri);
$response->assertOk();
$body = $response->json('data');
$this->assertEquals($body['state'], PirepState::CANCELLED);
} }
/** /**
@ -460,11 +475,6 @@ class AcarsTest extends TestCase
$body = $this->get('/api/pireps/'.$pirep_id)->json('data'); $body = $this->get('/api/pireps/'.$pirep_id)->json('data');
$this->assertNotNull($body['block_off_time']); $this->assertNotNull($body['block_off_time']);
$this->assertNotNull($body['block_on_time']); $this->assertNotNull($body['block_on_time']);
// make sure the time matches up
/*$block_on = new Carbon($body['block_on_time'], 'UTC');
$block_off = new Carbon($body['block_off_time'], 'UTC');
$this->assertEquals($block_on->subMinutes($body['flight_time']), $block_off);*/
} }
/** /**

View File

@ -17,6 +17,7 @@ use App\Services\ImportExport\AircraftExporter;
use App\Services\ImportExport\AirportExporter; use App\Services\ImportExport\AirportExporter;
use App\Services\ImportExport\FlightExporter; use App\Services\ImportExport\FlightExporter;
use App\Services\ImportService; use App\Services\ImportService;
use Illuminate\Validation\ValidationException;
/** /**
* Class ImporterTest * Class ImporterTest
@ -358,11 +359,10 @@ class ImporterTest extends TestCase
/** /**
* Try importing the aicraft in the airports. Should fail * Try importing the aicraft in the airports. Should fail
*
* @expectedException \Illuminate\Validation\ValidationException
*/ */
public function testInvalidFileImport(): void public function testInvalidFileImport(): void
{ {
$this->expectException(ValidationException::class);
$file_path = base_path('tests/data/aircraft.csv'); $file_path = base_path('tests/data/aircraft.csv');
$this->importSvc->importAirports($file_path); $this->importSvc->importAirports($file_path);
} }

View File

@ -95,6 +95,11 @@ class PIREPTest extends TestCase
$this->get('/api/fleet/aircraft/'.$pirep->aircraft_id, [], $user) $this->get('/api/fleet/aircraft/'.$pirep->aircraft_id, [], $user)
->assertJson(['data' => ['airport_id' => $pirep->arr_airport_id]]); ->assertJson(['data' => ['airport_id' => $pirep->arr_airport_id]]);
// Try cancelling it
$uri = '/api/pireps/'.$pirep->id.'/cancel';
$response = $this->put($uri, [], [], $user);
$response->assertStatus(400);
/** /**
* Now go from ACCEPTED to REJECTED * Now go from ACCEPTED to REJECTED
*/ */

View File

@ -1,5 +1,6 @@
<?php <?php
use App\Exceptions\UserPilotIdExists;
use App\Models\User; use App\Models\User;
use App\Repositories\SettingRepository; use App\Repositories\SettingRepository;
use App\Services\UserService; use App\Services\UserService;
@ -191,11 +192,10 @@ class UserTest extends TestCase
/** /**
* Test the pilot ID being added when a new user is created * Test the pilot ID being added when a new user is created
*
* @expectedException \App\Exceptions\UserPilotIdExists
*/ */
public function testUserPilotIdChangeAlreadyExists() public function testUserPilotIdChangeAlreadyExists()
{ {
$this->expectException(UserPilotIdExists::class);
$user1 = factory(App\Models\User::class)->create(['id' => 1]); $user1 = factory(App\Models\User::class)->create(['id' => 1]);
$user2 = factory(App\Models\User::class)->create(['id' => 2]); $user2 = factory(App\Models\User::class)->create(['id' => 2]);