Performance tracing with sentry
This commit is contained in:
parent
7b265c8133
commit
5bf4bde9f0
|
|
@ -214,7 +214,7 @@ class CreditController extends BaseController
|
||||||
if ($credit->invoice_id) {
|
if ($credit->invoice_id) {
|
||||||
$credit = $credit->service()->markSent()->save();
|
$credit = $credit->service()->markSent()->save();
|
||||||
// $credit->client->service()->updatePaidToDate(-1 * $credit->balance)->save(); // If we mutate the paid to date, we need to reverse the status of the invoice, this will allow the credit note that has been created to be used and double paid to dates prevented.
|
// $credit->client->service()->updatePaidToDate(-1 * $credit->balance)->save(); // If we mutate the paid to date, we need to reverse the status of the invoice, this will allow the credit note that has been created to be used and double paid to dates prevented.
|
||||||
$credit->client->service()->updateBalanceAndPaidToDate(-1 * $credit->balance, -1 * $credit->balance)->save();
|
$credit->client->service()->updateBalanceAndPaidToDate(-1 * ($credit->invoice->balance ?? 0), -1 * $credit->balance)->save();
|
||||||
// $invoice = $credit->invoice;
|
// $invoice = $credit->invoice;
|
||||||
|
|
||||||
$invoice = \App\Models\Invoice::withTrashed()->find($credit->invoice_id);
|
$invoice = \App\Models\Invoice::withTrashed()->find($credit->invoice_id);
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,7 @@ class ValidInvoiceCreditRule implements Rule
|
||||||
$cost = 0;
|
$cost = 0;
|
||||||
|
|
||||||
foreach (request()->input('line_items') as $item) {
|
foreach (request()->input('line_items') as $item) {
|
||||||
|
$item = (array)$item;
|
||||||
$cost += $item['cost'] * $item['quantity'];
|
$cost += $item['cost'] * $item['quantity'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -228,7 +228,7 @@ class TemplateAction implements ShouldQueue
|
||||||
|
|
||||||
public function middleware()
|
public function middleware()
|
||||||
{
|
{
|
||||||
return [new WithoutOverlapping("template-{$this->company->company_key}")];
|
return [(new WithoutOverlapping('template-' . $this->company->company_key . $this->entity))->releaseAfter(60)];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -621,6 +621,7 @@ class TemplateService
|
||||||
'payments' => $payments,
|
'payments' => $payments,
|
||||||
'total_tax_map' => $invoice->calc()->getTotalTaxMap(),
|
'total_tax_map' => $invoice->calc()->getTotalTaxMap(),
|
||||||
'line_tax_map' => $invoice->calc()->getTaxMap()->toArray(),
|
'line_tax_map' => $invoice->calc()->getTaxMap()->toArray(),
|
||||||
|
'project' => $invoice->project ? $this->transformProject($invoice->project, true) : [],
|
||||||
];
|
];
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
@ -1172,7 +1173,7 @@ class TemplateService
|
||||||
'client' => $this->getClient($project),
|
'client' => $this->getClient($project),
|
||||||
'user' => $this->userInfo($project->user),
|
'user' => $this->userInfo($project->user),
|
||||||
'assigned_user' => $project->assigned_user ? $this->userInfo($project->assigned_user) : [],
|
'assigned_user' => $project->assigned_user ? $this->userInfo($project->assigned_user) : [],
|
||||||
'invoices' => $this->processInvoices($project->invoices),
|
'invoices' => !$nested ? $this->processInvoices($project->invoices) : [],
|
||||||
'expenses' => ($project->expenses && !$nested) ? $this->processExpenses($project->expenses, true) : [],
|
'expenses' => ($project->expenses && !$nested) ? $this->processExpenses($project->expenses, true) : [],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,129 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sentry Laravel SDK configuration file.
|
||||||
|
*
|
||||||
|
* @see https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/
|
||||||
|
*/
|
||||||
return [
|
return [
|
||||||
|
|
||||||
//'dsn' => env('SENTRY_LARAVEL_DSN', env('SENTRY_DSN')),
|
// @see https://docs.sentry.io/product/sentry-basics/dsn-explainer/
|
||||||
'dsn' => config('ninja.sentry_dsn'),
|
'dsn' => config('ninja.sentry_dsn'),
|
||||||
|
|
||||||
|
// @see https://spotlightjs.com/
|
||||||
|
// 'spotlight' => env('SENTRY_SPOTLIGHT', false),
|
||||||
|
|
||||||
|
// @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#logger
|
||||||
|
// 'logger' => Sentry\Logger\DebugFileLogger::class, // By default this will log to `storage_path('logs/sentry.log')`
|
||||||
|
|
||||||
|
// The release version of your application
|
||||||
|
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
||||||
'release' => config('ninja.app_version'),
|
'release' => config('ninja.app_version'),
|
||||||
|
|
||||||
'breadcrumbs' => [
|
// When left empty or `null` the Laravel environment will be used (usually discovered from `APP_ENV` in your `.env`)
|
||||||
// Capture Laravel logs in breadcrumbs
|
'environment' => env('SENTRY_ENVIRONMENT'),
|
||||||
'logs' => true,
|
|
||||||
|
|
||||||
// Capture SQL queries in breadcrumbs
|
// @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#sample-rate
|
||||||
'sql_queries' => true,
|
'sample_rate' => env('SENTRY_SAMPLE_RATE') === null ? 1 : (float) env('SENTRY_SAMPLE_RATE'),
|
||||||
|
|
||||||
// Capture bindings on SQL queries logged in breadcrumbs
|
// @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#traces-sample-rate
|
||||||
'sql_bindings' => true,
|
'traces_sample_rate' => env('SENTRY_TRACES_SAMPLE_RATE') === null ? 0.01 : (float) env('SENTRY_TRACES_SAMPLE_RATE'),
|
||||||
|
|
||||||
// Capture queue job information in breadcrumbs
|
// @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#profiles-sample-rate
|
||||||
'queue_info' => true,
|
'profiles_sample_rate' => env('SENTRY_PROFILES_SAMPLE_RATE') === null ? 0.001 : (float) env('SENTRY_PROFILES_SAMPLE_RATE'),
|
||||||
|
|
||||||
// Capture command information in breadcrumbs
|
// @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#send-default-pii
|
||||||
'command_info' => true,
|
'send_default_pii' => env('SENTRY_SEND_DEFAULT_PII', false),
|
||||||
|
|
||||||
|
// @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#ignore-exceptions
|
||||||
|
// 'ignore_exceptions' => [],
|
||||||
|
|
||||||
|
// @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#ignore-transactions
|
||||||
|
'ignore_transactions' => [
|
||||||
|
// Ignore Laravel's default health URL
|
||||||
|
'/up',
|
||||||
|
],
|
||||||
|
|
||||||
|
// Breadcrumb specific configuration
|
||||||
|
'breadcrumbs' => [
|
||||||
|
// Capture Laravel logs as breadcrumbs
|
||||||
|
'logs' => env('SENTRY_BREADCRUMBS_LOGS_ENABLED', true),
|
||||||
|
|
||||||
|
// Capture Laravel cache events (hits, writes etc.) as breadcrumbs
|
||||||
|
'cache' => env('SENTRY_BREADCRUMBS_CACHE_ENABLED', true),
|
||||||
|
|
||||||
|
// Capture Livewire components like routes as breadcrumbs
|
||||||
|
'livewire' => env('SENTRY_BREADCRUMBS_LIVEWIRE_ENABLED', true),
|
||||||
|
|
||||||
|
// Capture SQL queries as breadcrumbs
|
||||||
|
'sql_queries' => env('SENTRY_BREADCRUMBS_SQL_QUERIES_ENABLED', true),
|
||||||
|
|
||||||
|
// Capture SQL query bindings (parameters) in SQL query breadcrumbs
|
||||||
|
'sql_bindings' => env('SENTRY_BREADCRUMBS_SQL_BINDINGS_ENABLED', false),
|
||||||
|
|
||||||
|
// Capture queue job information as breadcrumbs
|
||||||
|
'queue_info' => env('SENTRY_BREADCRUMBS_QUEUE_INFO_ENABLED', true),
|
||||||
|
|
||||||
|
// Capture command information as breadcrumbs
|
||||||
|
'command_info' => env('SENTRY_BREADCRUMBS_COMMAND_JOBS_ENABLED', true),
|
||||||
|
|
||||||
|
// Capture HTTP client request information as breadcrumbs
|
||||||
|
'http_client_requests' => env('SENTRY_BREADCRUMBS_HTTP_CLIENT_REQUESTS_ENABLED', true),
|
||||||
|
|
||||||
|
// Capture send notifications as breadcrumbs
|
||||||
|
'notifications' => env('SENTRY_BREADCRUMBS_NOTIFICATIONS_ENABLED', true),
|
||||||
|
],
|
||||||
|
|
||||||
|
// Performance monitoring specific configuration
|
||||||
|
'tracing' => [
|
||||||
|
// Trace queue jobs as their own transactions (this enables tracing for queue jobs)
|
||||||
|
'queue_job_transactions' => env('SENTRY_TRACE_QUEUE_ENABLED', true),
|
||||||
|
|
||||||
|
// Capture queue jobs as spans when executed on the sync driver
|
||||||
|
'queue_jobs' => env('SENTRY_TRACE_QUEUE_JOBS_ENABLED', true),
|
||||||
|
|
||||||
|
// Capture SQL queries as spans
|
||||||
|
'sql_queries' => env('SENTRY_TRACE_SQL_QUERIES_ENABLED', true),
|
||||||
|
|
||||||
|
// Capture SQL query bindings (parameters) in SQL query spans
|
||||||
|
'sql_bindings' => env('SENTRY_TRACE_SQL_BINDINGS_ENABLED', false),
|
||||||
|
|
||||||
|
// Capture where the SQL query originated from on the SQL query spans
|
||||||
|
'sql_origin' => env('SENTRY_TRACE_SQL_ORIGIN_ENABLED', true),
|
||||||
|
|
||||||
|
// Define a threshold in milliseconds for SQL queries to resolve their origin
|
||||||
|
'sql_origin_threshold_ms' => env('SENTRY_TRACE_SQL_ORIGIN_THRESHOLD_MS', 100),
|
||||||
|
|
||||||
|
// Capture views rendered as spans
|
||||||
|
'views' => env('SENTRY_TRACE_VIEWS_ENABLED', true),
|
||||||
|
|
||||||
|
// Capture Livewire components as spans
|
||||||
|
'livewire' => env('SENTRY_TRACE_LIVEWIRE_ENABLED', true),
|
||||||
|
|
||||||
|
// Capture HTTP client requests as spans
|
||||||
|
'http_client_requests' => env('SENTRY_TRACE_HTTP_CLIENT_REQUESTS_ENABLED', true),
|
||||||
|
|
||||||
|
// Capture Laravel cache events (hits, writes etc.) as spans
|
||||||
|
'cache' => env('SENTRY_TRACE_CACHE_ENABLED', true),
|
||||||
|
|
||||||
|
// Capture Redis operations as spans (this enables Redis events in Laravel)
|
||||||
|
'redis_commands' => env('SENTRY_TRACE_REDIS_COMMANDS', false),
|
||||||
|
|
||||||
|
// Capture where the Redis command originated from on the Redis command spans
|
||||||
|
'redis_origin' => env('SENTRY_TRACE_REDIS_ORIGIN_ENABLED', true),
|
||||||
|
|
||||||
|
// Capture send notifications as spans
|
||||||
|
'notifications' => env('SENTRY_TRACE_NOTIFICATIONS_ENABLED', true),
|
||||||
|
|
||||||
|
// Enable tracing for requests without a matching route (404's)
|
||||||
|
'missing_routes' => env('SENTRY_TRACE_MISSING_ROUTES_ENABLED', false),
|
||||||
|
|
||||||
|
// Configures if the performance trace should continue after the response has been sent to the user until the application terminates
|
||||||
|
// This is required to capture any spans that are created after the response has been sent like queue jobs dispatched using `dispatch(...)->afterResponse()` for example
|
||||||
|
'continue_after_response' => env('SENTRY_TRACE_CONTINUE_AFTER_RESPONSE', true),
|
||||||
|
|
||||||
|
// Enable the tracing integrations supplied by Sentry (recommended)
|
||||||
|
'default_integrations' => env('SENTRY_TRACE_DEFAULT_INTEGRATIONS_ENABLED', true),
|
||||||
],
|
],
|
||||||
|
|
||||||
// @see: https://docs.sentry.io/error-reporting/configuration/?platform=php#send-default-pii
|
|
||||||
'send_default_pii' => false,
|
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,160 @@ class CreditTest extends TestCase
|
||||||
$this->makeTestData();
|
$this->makeTestData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testCreditReversalScenarioInvoicePartiallyPaid()
|
||||||
|
{
|
||||||
|
|
||||||
|
$c = Client::factory()->create([
|
||||||
|
'company_id' => $this->company->id,
|
||||||
|
'user_id' => $this->user->id,
|
||||||
|
'balance' => 0,
|
||||||
|
'paid_to_date' => 0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$ii = new InvoiceItem();
|
||||||
|
$ii->cost = 100;
|
||||||
|
$ii->quantity = 1;
|
||||||
|
$ii->product_key = 'xx';
|
||||||
|
$ii->notes = 'yy';
|
||||||
|
|
||||||
|
$i = \App\Models\Invoice::factory()->create([
|
||||||
|
'company_id' => $this->company->id,
|
||||||
|
'user_id' => $this->user->id,
|
||||||
|
'client_id' => $c->id,
|
||||||
|
'tax_name1' => '',
|
||||||
|
'tax_name2' => '',
|
||||||
|
'tax_name3' => '',
|
||||||
|
'tax_rate1' => 0,
|
||||||
|
'tax_rate2' => 0,
|
||||||
|
'tax_rate3' => 0,
|
||||||
|
'discount' => 0,
|
||||||
|
'line_items' => [
|
||||||
|
$ii
|
||||||
|
],
|
||||||
|
'status_id' => 1,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$i = $i->calc()->getInvoice();
|
||||||
|
$i = $i->service()->markSent()->save();
|
||||||
|
|
||||||
|
$this->assertEquals(100, $i->balance);
|
||||||
|
$this->assertEquals(100, $i->amount);
|
||||||
|
|
||||||
|
$i->service()->applyPaymentAmount(50, 'test');
|
||||||
|
$i->refresh();
|
||||||
|
|
||||||
|
$this->assertEquals(50, $i->balance);
|
||||||
|
$this->assertEquals(100, $i->amount);
|
||||||
|
|
||||||
|
$credit_array = $i->withoutRelations()->toArray();
|
||||||
|
$credit_array['invoice_id'] = $i->hashed_id;
|
||||||
|
$credit_array['client_id'] = $c->hashed_id;
|
||||||
|
|
||||||
|
$ii = new InvoiceItem();
|
||||||
|
$ii->cost = 50;
|
||||||
|
$ii->quantity = 1;
|
||||||
|
$ii->product_key = 'xx';
|
||||||
|
$ii->notes = 'yy';
|
||||||
|
|
||||||
|
$credit_array['line_items'] = [$ii];
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->post('/api/v1/credits', $credit_array);
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
$arr = $response->json();
|
||||||
|
|
||||||
|
$this->assertEquals(50, $arr['data']['balance']);
|
||||||
|
$this->assertEquals(50, $arr['data']['amount']);
|
||||||
|
$this->assertEquals(2, $arr['data']['status_id']);
|
||||||
|
|
||||||
|
$i->refresh();
|
||||||
|
$c->refresh();
|
||||||
|
|
||||||
|
$this->assertEquals(0, $c->balance);
|
||||||
|
$this->assertEquals(6, $i->status_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function testCreditReversalScenarioInvoicePaidInFull()
|
||||||
|
{
|
||||||
|
|
||||||
|
$c = Client::factory()->create([
|
||||||
|
'company_id' => $this->company->id,
|
||||||
|
'user_id' => $this->user->id,
|
||||||
|
'balance' => 0,
|
||||||
|
'paid_to_date' => 0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$ii = new InvoiceItem();
|
||||||
|
$ii->cost = 100;
|
||||||
|
$ii->quantity = 1;
|
||||||
|
$ii->product_key = 'xx';
|
||||||
|
$ii->notes = 'yy';
|
||||||
|
|
||||||
|
$i = \App\Models\Invoice::factory()->create([
|
||||||
|
'company_id' => $this->company->id,
|
||||||
|
'user_id' => $this->user->id,
|
||||||
|
'client_id' => $c->id,
|
||||||
|
'tax_name1' => '',
|
||||||
|
'tax_name2' => '',
|
||||||
|
'tax_name3' => '',
|
||||||
|
'tax_rate1' => 0,
|
||||||
|
'tax_rate2' => 0,
|
||||||
|
'tax_rate3' => 0,
|
||||||
|
'discount' => 0,
|
||||||
|
'line_items' => [
|
||||||
|
$ii
|
||||||
|
],
|
||||||
|
'status_id' => 1,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$i = $i->calc()->getInvoice();
|
||||||
|
$i = $i->service()->markSent()->save();
|
||||||
|
|
||||||
|
$this->assertEquals(100, $i->balance);
|
||||||
|
$this->assertEquals(100, $i->amount);
|
||||||
|
|
||||||
|
$i->service()->applyPaymentAmount(100, 'test');
|
||||||
|
$i->refresh();
|
||||||
|
|
||||||
|
$this->assertEquals(0, $i->balance);
|
||||||
|
$this->assertEquals(100, $i->amount);
|
||||||
|
$this->assertEquals(4, $i->status_id);
|
||||||
|
|
||||||
|
$credit_array = $i->withoutRelations()->toArray();
|
||||||
|
$credit_array['invoice_id'] = $i->hashed_id;
|
||||||
|
$credit_array['client_id'] = $c->hashed_id;
|
||||||
|
|
||||||
|
$ii = new InvoiceItem();
|
||||||
|
$ii->cost = 100;
|
||||||
|
$ii->quantity = 1;
|
||||||
|
$ii->product_key = 'xx';
|
||||||
|
$ii->notes = 'yy';
|
||||||
|
|
||||||
|
$credit_array['line_items'] = [$ii];
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->post('/api/v1/credits', $credit_array);
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
$arr = $response->json();
|
||||||
|
|
||||||
|
$this->assertEquals(100, $arr['data']['balance']);
|
||||||
|
$this->assertEquals(100, $arr['data']['amount']);
|
||||||
|
$this->assertEquals(2, $arr['data']['status_id']);
|
||||||
|
|
||||||
|
$i->refresh();
|
||||||
|
$c->refresh();
|
||||||
|
|
||||||
|
$this->assertEquals(0, $c->balance);
|
||||||
|
$this->assertEquals(6, $i->status_id);
|
||||||
|
}
|
||||||
|
|
||||||
public function testPaidToDateAdjustments()
|
public function testPaidToDateAdjustments()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue