diff --git a/app/Filters/SystemLogFilters.php b/app/Filters/SystemLogFilters.php new file mode 100644 index 000000000..57c9f06cd --- /dev/null +++ b/app/Filters/SystemLogFilters.php @@ -0,0 +1,110 @@ +builder->where('type_id', $type_id); + } + + public function category_id(int $category_id) :Builder + { + return $this->builder->where('category_id', $category_id); + } + + public function event_id(int $event_id) :Builder + { + return $this->builder->where('event_id', $event_id); + } + + public function client_id(int $client_id) :Builder + { + return $this->builder->where('client_id', $client_id); + } + + /** + * Filter based on search text + * + * @param string query filter + * @return Illuminate\Database\Query\Builder + * @deprecated + * + */ + public function filter(string $filter = '') : Builder + { + if (strlen($filter) == 0) { + return $this->builder; + } + + return $this->builder; + + // return $this->builder->where(function ($query) use ($filter) { + // $query->where('vendors.name', 'like', '%'.$filter.'%') + // ->orWhere('vendors.id_number', 'like', '%'.$filter.'%') + // ->orWhere('vendor_contacts.first_name', 'like', '%'.$filter.'%') + // ->orWhere('vendor_contacts.last_name', 'like', '%'.$filter.'%') + // ->orWhere('vendor_contacts.email', 'like', '%'.$filter.'%') + // ->orWhere('vendors.custom_value1', 'like', '%'.$filter.'%') + // ->orWhere('vendors.custom_value2', 'like', '%'.$filter.'%') + // ->orWhere('vendors.custom_value3', 'like', '%'.$filter.'%') + // ->orWhere('vendors.custom_value4', 'like', '%'.$filter.'%'); + // }); + } + + /** + * Sorts the list based on $sort + * + * @param string sort formatted as column|asc + * @return Illuminate\Database\Query\Builder + */ + public function sort(string $sort) : Builder + { + $sort_col = explode("|", $sort); + return $this->builder->orderBy($sort_col[0], $sort_col[1]); + } + + /** + * Returns the base query + * + * @param int company_id + * @return Illuminate\Database\Query\Builder + * @deprecated + */ + public function baseQuery(int $company_id, User $user) : Builder + { + } + + /** + * Filters the query by the users company ID + * + * @param $company_id The company Id + * @return Illuminate\Database\Query\Builder + */ + public function entityFilter() + { + + //return $this->builder->whereCompanyId(auth()->user()->company()->id); + return $this->builder->company(); + } +} diff --git a/app/Filters/VendorFilters.php b/app/Filters/VendorFilters.php index 183232a1e..769608d49 100644 --- a/app/Filters/VendorFilters.php +++ b/app/Filters/VendorFilters.php @@ -23,32 +23,6 @@ use Illuminate\Support\Facades\Gate; class VendorFilters extends QueryFilters { - /** - * Filter by balance - * - * @param string $balance - * @return Illuminate\Database\Query\Builder - */ - public function balance(string $balance): Builder - { - $parts = $this->split($balance); - - return $this->builder->where('balance', $parts->operator, $parts->value); - } - - /** - * Filter between balances - * - * @param string balance - * @return Illuminate\Database\Query\Builder - */ - public function between_balance(string $balance): Builder - { - $parts = explode(":", $balance); - - return $this->builder->whereBetween('balance', [$parts[0], $parts[1]]); - } - /** * Filter based on search text * diff --git a/app/Http/Controllers/MigrationController.php b/app/Http/Controllers/MigrationController.php index 1e1b363c6..4b4c755cb 100644 --- a/app/Http/Controllers/MigrationController.php +++ b/app/Http/Controllers/MigrationController.php @@ -146,8 +146,8 @@ class MigrationController extends BaseController public function purgeCompanySaveSettings(Request $request, Company $company) { - $company->clients()->delete(); - $company->products()->delete(); + $company->clients()->forceDelete(); + $company->products()->forceDelete(); $company->save(); diff --git a/app/Http/Controllers/OpenAPI/SystemLogSchema.php b/app/Http/Controllers/OpenAPI/SystemLogSchema.php new file mode 100644 index 000000000..dd1a1cb18 --- /dev/null +++ b/app/Http/Controllers/OpenAPI/SystemLogSchema.php @@ -0,0 +1,17 @@ +listResponse($system_logs); + } + + /** + * Show the form for creating a new resource. + * + * @return \Illuminate\Http\Response + */ + public function create() + { + $error = [ + 'message' => 'Cannot create system log', + 'errors' => new \stdClass + ]; + + + return response()->json($error, 400); + } + + /** + * Store a newly created resource in storage. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ + public function store(Request $request) + { + + $error = [ + 'message' => 'Cannot store system log', + 'errors' => new \stdClass + ]; + + + return response()->json($error, 400); + + } + + /** + * Display the specified resource. + * + * @param \App\Http\Requests\Invoice\ShowInvoiceRequest $request The request + * @param \App\Models\SystemLog $system_logs The system logs + * + * @return \Illuminate\Http\Response + * + * + * @OA\Get( + * path="/api/v1/system_logs/{id}", + * operationId="showSystemLogs", + * tags={"system_logs"}, + * summary="Shows a system_logs", + * description="Displays a system_logs by id", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Parameter( + * name="id", + * in="path", + * description="The system_logs Hashed ID", + * example="D2J234DFA", + * required=true, + * @OA\Schema( + * type="string", + * format="string", + * ), + * ), + * @OA\Response( + * response=200, + * description="Returns the system_logs object", + * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * @OA\JsonContent(ref="#/components/schemas/SystemLog"), + * ), + * @OA\Response( + * response=422, + * description="Validation error", + * @OA\JsonContent(ref="#/components/schemas/ValidationError"), + * + * ), + * @OA\Response( + * response="default", + * description="Unexpected Error", + * @OA\JsonContent(ref="#/components/schemas/Error"), + * ), + * ) + * + */ + public function show(Request $request, SystemLog $system_log) + { + return $this->itemResponse($system_log); + } + + /** + * Show the form for editing the specified resource. + * + * @param int $id + * @return \Illuminate\Http\Response + */ + public function edit($id) + { + $error = [ + 'message' => 'Cannot edit system log', + 'errors' => new \stdClass + ]; + + + return response()->json($error, 400); + } + + /** + * Update the specified resource in storage. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\Response + */ + public function update(Request $request, $id) + { + $error = [ + 'message' => 'Cannot update system log', + 'errors' => new \stdClass + ]; + + + return response()->json($error, 400); + } + + /** + * Remove the specified resource from storage. + * + * @param int $id + * @return \Illuminate\Http\Response + */ + public function destroy($id) + { + $error = [ + 'message' => 'Cannot destroy system log', + 'errors' => new \stdClass + ]; + + + return response()->json($error, 400); + } +} diff --git a/app/Http/Requests/Credit/UniqueCreditNumberRule.php b/app/Http/ValidationRules/Credit/UniqueCreditNumberRule.php similarity index 100% rename from app/Http/Requests/Credit/UniqueCreditNumberRule.php rename to app/Http/ValidationRules/Credit/UniqueCreditNumberRule.php diff --git a/app/Http/Requests/Quote/UniqueQuoteNumberRule.php b/app/Http/ValidationRules/Quote/UniqueQuoteNumberRule.php similarity index 100% rename from app/Http/Requests/Quote/UniqueQuoteNumberRule.php rename to app/Http/ValidationRules/Quote/UniqueQuoteNumberRule.php diff --git a/app/Jobs/Util/WebhookHandler.php b/app/Jobs/Util/WebhookHandler.php index e691a0773..a8f5f4795 100644 --- a/app/Jobs/Util/WebhookHandler.php +++ b/app/Jobs/Util/WebhookHandler.php @@ -64,7 +64,6 @@ class WebhookHandler implements ShouldQueue // generate JSON data $manager = new Manager(); $manager->setSerializer(new ArraySerializer()); - // $manager->parseIncludes($include); $transformer = new $this->getTransformerClassName(); @@ -101,7 +100,8 @@ class WebhookHandler implements ShouldQueue public function failed($exception) { - $exception->getMessage(); - // etc... + + info(print_r($exception->getMessage(),1)); + } } diff --git a/app/Models/Client.php b/app/Models/Client.php index ecead74d7..829c97603 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -202,6 +202,11 @@ class Client extends BaseModel implements HasLocalePreference return $this->belongsTo(Country::class, 'shipping_country_id', 'id'); } + public function system_logs() + { + return $this->hasMany(SystemLog::class); + } + public function timezone() { return Timezone::find($this->getSetting('timezone_id')); diff --git a/app/Models/Company.php b/app/Models/Company.php index d54f712a9..902799957 100644 --- a/app/Models/Company.php +++ b/app/Models/Company.php @@ -391,6 +391,11 @@ class Company extends BaseModel return $this->hasMany(CompanyToken::class); } + public function system_logs() + { + return $this->hasMany(SystemLog::class); + } + public function tokens_hashed() { return $this->hasMany(CompanyToken::class); diff --git a/app/Models/SystemLog.php b/app/Models/SystemLog.php index f7b517314..25d082952 100644 --- a/app/Models/SystemLog.php +++ b/app/Models/SystemLog.php @@ -11,10 +11,26 @@ namespace App\Models; +use App\Models\Filterable; +use App\Utils\Traits\MakesHash; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\SoftDeletes; class SystemLog extends Model { + use Filterable; + use SoftDeletes; + use MakesHash; + + protected $casts = [ + 'updated_at' => 'timestamp', + 'created_at' => 'timestamp', + 'deleted_at' => 'timestamp', + 'log' => 'array' + ]; + + protected $dateFormat = 'Y-m-d H:i:s.u'; + /* Category IDs */ const CATEGORY_GATEWAY_RESPONSE = 1; const CATEGORY_MAIL = 2; @@ -51,7 +67,23 @@ class SystemLog extends Model 'type_id', ]; - protected $casts = [ - 'log' => 'array' - ]; + public function resolveRouteBinding($value) + { + if (is_numeric($value)) { + throw new ModelNotFoundException("Record with value {$value} not found"); + } + + return $this + ->where('id', $this->decodePrimaryKey($value))->firstOrFail(); + } + + /* + V2 type of scope + */ + public function scopeCompany($query) + { + $query->where('company_id', auth()->user()->companyId()); + + return $query; + } } diff --git a/app/Transformers/ClientTransformer.php b/app/Transformers/ClientTransformer.php index 0f06ab572..cc533fc25 100644 --- a/app/Transformers/ClientTransformer.php +++ b/app/Transformers/ClientTransformer.php @@ -17,10 +17,12 @@ use App\Models\ClientContact; use App\Models\ClientGatewayToken; use App\Models\CompanyLedger; use App\Models\Document; +use App\Models\SystemLog; use App\Transformers\ActivityTransformer; use App\Transformers\ClientGatewayTokenTransformer; use App\Transformers\CompanyLedgerTransformer; use App\Transformers\DocumentTransformer; +use App\Transformers\SystemLogTransformer; use App\Utils\Traits\MakesHash; /** @@ -44,6 +46,7 @@ class ClientTransformer extends EntityTransformer 'gateway_tokens', 'activities', 'ledger', + 'system_logs', ]; @@ -91,6 +94,14 @@ class ClientTransformer extends EntityTransformer return $this->includeCollection($client->ledger, $transformer, CompanyLedger::class); } + + public function includeSystemLogs(Client $client) + { + $transformer = new SystemLogTransformer($this->serializer); + + return $this->includeCollection($client->system_logs, $transformer, SystemLog::class); + } + /** * @param Client $client * diff --git a/app/Transformers/CompanyGatewayTransformer.php b/app/Transformers/CompanyGatewayTransformer.php index 8261d2d4e..e14c9ffa0 100644 --- a/app/Transformers/CompanyGatewayTransformer.php +++ b/app/Transformers/CompanyGatewayTransformer.php @@ -27,7 +27,6 @@ class CompanyGatewayTransformer extends EntityTransformer * @var array */ protected $defaultIncludes = [ - 'gateway' ]; /** @@ -63,6 +62,8 @@ class CompanyGatewayTransformer extends EntityTransformer 'custom_value2' => $company_gateway->custom_value2 ?: '', 'custom_value3' => $company_gateway->custom_value3 ?: '', 'custom_value4' => $company_gateway->custom_value4 ?: '', + 'label' => (string)$company_gateway->label ?: '', + 'token_billing' => (string)$company_gateway->token_billing, ]; } diff --git a/app/Transformers/CompanyTransformer.php b/app/Transformers/CompanyTransformer.php index 513aa5687..1863a6a78 100644 --- a/app/Transformers/CompanyTransformer.php +++ b/app/Transformers/CompanyTransformer.php @@ -29,8 +29,9 @@ use App\Models\PaymentTerm; use App\Models\Product; use App\Models\Project; use App\Models\Quote; +use App\Models\SystemLog; use App\Models\Task; -use App\Models\TaxRate; +use App\Models\TaxRate; use App\Models\User; use App\Models\Webhook; use App\Transformers\CompanyLedgerTransformer; @@ -39,6 +40,7 @@ use App\Transformers\CompanyTokenTransformer; use App\Transformers\CreditTransformer; use App\Transformers\DocumentTransformer; use App\Transformers\PaymentTermTransformer; +use App\Transformers\SystemLogTransformer; use App\Transformers\TaskTransformer; use App\Transformers\WebhookTransformer; use App\Utils\Traits\MakesHash; @@ -88,7 +90,8 @@ class CompanyTransformer extends EntityTransformer 'ledger', 'webhooks', 'tokens', - 'tokens_hashed' + 'tokens_hashed', + 'system_logs', ]; @@ -307,4 +310,11 @@ class CompanyTransformer extends EntityTransformer return $this->includeCollection($company->payment_terms()->get(), $transformer, PaymentTerm::class); } + + public function includeSystemLogs(Company $company) + { + $transformer = new SystemLogTransformer($this->serializer); + + return $this->includeCollection($company->system_logs, $transformer, SystemLog::class); + } } diff --git a/app/Transformers/SystemLogTransformer.php b/app/Transformers/SystemLogTransformer.php new file mode 100644 index 000000000..1e286d3ff --- /dev/null +++ b/app/Transformers/SystemLogTransformer.php @@ -0,0 +1,39 @@ + (string) $this->encodePrimaryKey($system_log->id), + 'company_id' => (string) $this->encodePrimaryKey($system_log->company_id), + 'user_id' => (string) $this->encodePrimaryKey($system_log->user_id), + 'client_id' => (string) $this->encodePrimaryKey($system_log->client_id), + 'event_id' => (int) $system_log->event_id, + 'category_id' => (int) $system_log->category_id, + 'type_id' => (int) $system_log->type_id, + 'log' => (string) $system_log->log, + 'updated_at' => (int)$system_log->updated_at, + 'created_at' => (int)$system_log->created_at, + ]; + } +} diff --git a/database/migrations/2014_10_13_000000_create_users_table.php b/database/migrations/2014_10_13_000000_create_users_table.php index 1141ee17b..2dda97aaa 100644 --- a/database/migrations/2014_10_13_000000_create_users_table.php +++ b/database/migrations/2014_10_13_000000_create_users_table.php @@ -481,7 +481,7 @@ class CreateUsersTable extends Migration $t->boolean('custom_surcharge_tax3')->default(false); $t->boolean('custom_surcharge_tax4')->default(false); - $t->decimal('exchange_rate', 16, 4); + $t->decimal('exchange_rate', 13, 6)->default(1); $t->decimal('amount', 16, 4); $t->decimal('balance', 16, 4); $t->decimal('partial', 16, 4)->nullable(); @@ -559,7 +559,7 @@ class CreateUsersTable extends Migration $t->boolean('custom_surcharge_tax3')->default(false); $t->boolean('custom_surcharge_tax4')->default(false); - $t->decimal('exchange_rate', 16, 4); + $t->decimal('exchange_rate', 13, 6)->default(1); $t->decimal('amount', 16, 4); $t->decimal('balance', 16, 4); $t->decimal('partial', 16, 4)->nullable(); @@ -803,7 +803,7 @@ class CreateUsersTable extends Migration $t->boolean('custom_surcharge_tax3')->default(false); $t->boolean('custom_surcharge_tax4')->default(false); - $t->decimal('exchange_rate', 16, 4); + $t->decimal('exchange_rate', 13, 6)->default(1); $t->decimal('amount', 16, 4); $t->decimal('balance', 16, 4); $t->decimal('partial', 16, 4)->nullable(); @@ -964,7 +964,7 @@ class CreateUsersTable extends Migration $t->softDeletes('deleted_at', 6); $t->boolean('is_deleted')->default(false); $t->boolean('is_manual')->default(false); - $t->decimal('exchange_rate', 16, 6)->default(1); + $t->decimal('exchange_rate', 13, 6)->default(1); $t->unsignedInteger('currency_id'); $t->unsignedInteger('exchange_currency_id'); @@ -1297,7 +1297,7 @@ class CreateUsersTable extends Migration $table->boolean('is_deleted')->default(false); $table->decimal('amount', 13, 2); $table->decimal('foreign_amount', 13, 2); - $table->decimal('exchange_rate', 13, 4); + $table->decimal('exchange_rate', 13, 6)->default(1); $table->string('tax_name1')->nullable(); $table->decimal('tax_rate1', 13, 3)->default(0); $table->string('tax_name2')->nullable(); diff --git a/database/migrations/2020_08_18_140557_add_is_public_to_documents_table.php b/database/migrations/2020_08_18_140557_add_is_public_to_documents_table.php index 1079ab6c6..784db3ae3 100644 --- a/database/migrations/2020_08_18_140557_add_is_public_to_documents_table.php +++ b/database/migrations/2020_08_18_140557_add_is_public_to_documents_table.php @@ -31,6 +31,11 @@ class AddIsPublicToDocumentsTable extends Migration $table->text('meta')->nullable(); }); + Schema::table('system_logs', function (Blueprint $table) { + $table->softDeletes('deleted_at', 6); + }); + + } /** diff --git a/routes/api.php b/routes/api.php index cf06aa78e..536fc1934 100644 --- a/routes/api.php +++ b/routes/api.php @@ -146,6 +146,8 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a // Route::delete('hooks/{subscription_id}', 'SubscriptionController@unsubscribe')->name('hooks.unsubscribe'); Route::resource('webhooks', 'WebhookController'); + Route::resource('system_logs', 'SystemLogController'); + Route::post('webhooks/bulk', 'WebhookController@bulk')->name('webhooks.bulk'); /*Company Ledger */ diff --git a/tests/Feature/SystemLogApiTest.php b/tests/Feature/SystemLogApiTest.php new file mode 100644 index 000000000..4eeada3ed --- /dev/null +++ b/tests/Feature/SystemLogApiTest.php @@ -0,0 +1,127 @@ +makeTestData(); + + } + + + public function testSystemLogRoutes() + { + + $sl = [ + 'client_id' => $this->client->id, + 'company_id' => $this->company->id, + 'user_id' => $this->client->user_id, + 'log' => 'thelog', + 'category_id' => 1, + 'event_id' => 1, + 'type_id' => 1, + ]; + + SystemLog::create($sl); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token + ])->get('/api/v1/system_logs'); + + + $response->assertStatus(200); + + $arr = $response->json(); + + $this->assertTrue(count($arr['data']) >=1); + + $hashed_id = $arr['data'][0]['id']; + + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token + ])->get('/api/v1/system_logs/' . $hashed_id); + + $response->assertStatus(200); + + $arr = $response->json(); + + $this->assertEquals($hashed_id, $arr['data']['id']); + + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token + ])->put('/api/v1/system_logs/' . $hashed_id, $sl)->assertStatus(400); + + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token + ])->delete('/api/v1/system_logs/' . $hashed_id)->assertStatus(400); + } + + public function testStoreRouteFails() + { + + $sl = [ + 'client_id' => $this->client->id, + 'company_id' => $this->company->id, + 'user_id' => $this->client->user_id, + 'log' => 'thelog', + 'category_id' => 1, + 'event_id' => 1, + 'type_id' => 1, + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token + ])->post('/api/v1/system_logs', $sl)->assertStatus(400); + + } + + public function testCreateRouteFails() + { + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token + ])->get('/api/v1/system_logs/create')->assertStatus(400); + + } + +}