diff --git a/app/DataMapper/Schedule/EmailProductSalesReport.php b/app/DataMapper/Schedule/EmailReport.php similarity index 92% rename from app/DataMapper/Schedule/EmailProductSalesReport.php rename to app/DataMapper/Schedule/EmailReport.php index c8b06253db26..bba351eeb47f 100644 --- a/app/DataMapper/Schedule/EmailProductSalesReport.php +++ b/app/DataMapper/Schedule/EmailReport.php @@ -11,14 +11,14 @@ namespace App\DataMapper\Schedule; -class EmailProductSalesReport +class EmailReport { /** * Defines the template name * * @var string */ - public string $template = 'email_product_sales_report'; + public string $template = 'email_report'; /** * An array of clients hashed_ids @@ -66,5 +66,8 @@ class EmailProductSalesReport * @var string */ public string $end_date = ''; + + /** @var string $report_name */ + public string $report_name = ''; } diff --git a/app/Http/Requests/TaskScheduler/StoreSchedulerRequest.php b/app/Http/Requests/TaskScheduler/StoreSchedulerRequest.php index 48d5073f4f3a..f3cbd13e9ffa 100644 --- a/app/Http/Requests/TaskScheduler/StoreSchedulerRequest.php +++ b/app/Http/Requests/TaskScheduler/StoreSchedulerRequest.php @@ -45,6 +45,7 @@ class StoreSchedulerRequest extends Request 'parameters.end_date' => ['bail', 'sometimes', 'date:Y-m-d', 'required_if:parameters.date_rate,custom', 'after_or_equal:parameters.start_date'], 'parameters.entity' => ['bail', 'sometimes', 'string', 'in:invoice,credit,quote,purchase_order'], 'parameters.entity_id' => ['bail', 'sometimes', 'string'], + 'parameters.report_name' => ['bail','sometimes', 'string', 'required_if:template,email_report', 'in:ar_summary_report,ar_detail_report,tax_summary_report,user_sales_report,client_sales_report,client_balance_report,product_sales_report'], ]; return $rules; diff --git a/app/Services/Scheduler/EmailProductSalesReport.php b/app/Services/Scheduler/EmailReport.php similarity index 57% rename from app/Services/Scheduler/EmailProductSalesReport.php rename to app/Services/Scheduler/EmailReport.php index 596308601bd0..2913782d9f84 100644 --- a/app/Services/Scheduler/EmailProductSalesReport.php +++ b/app/Services/Scheduler/EmailReport.php @@ -19,9 +19,14 @@ use App\Utils\Traits\MakesDates; use App\Jobs\Mail\NinjaMailerJob; use App\Jobs\Mail\NinjaMailerObject; use App\Export\CSV\ProductSalesExport; -use App\DataMapper\Schedule\EmailStatement; +use App\Services\Report\ARDetailReport; +use App\Services\Report\ARSummaryReport; +use App\Services\Report\ClientBalanceReport; +use App\Services\Report\ClientSalesReport; +use App\Services\Report\TaxSummaryReport; +use App\Services\Report\UserSalesReport; -class EmailProductSalesReport +class EmailReport { use MakesHash; use MakesDates; @@ -30,7 +35,7 @@ class EmailProductSalesReport private bool $multiple_clients = false; - private string $file_name = 'product_sales.csv'; + private string $file_name = 'file.csv'; public function __construct(public Scheduler $scheduler) { @@ -54,7 +59,25 @@ class EmailProductSalesReport $data['clients'] = $this->transformKeys($this->scheduler->parameters['clients']); } - $export = (new ProductSalesExport($this->scheduler->company, $data)); + $export = false; + + match($this->scheduler->parameters['report_name']) + { + 'product_sales_report' => $export = (new ProductSalesExport($this->scheduler->company, $data)), + 'email_ar_detailed_report' => (new ARDetailReport($this->scheduler->company, $data)), + 'email_ar_summary_report' => (new ARSummaryReport($this->scheduler->company, $data)), + 'email_tax_summary_report' => (new TaxSummaryReport($this->scheduler->company, $data)), + 'email_client_balance_report' => (new ClientBalanceReport($this->scheduler->company, $data)), + 'email_client_sales_report' => (new ClientSalesReport($this->scheduler->company, $data)), + 'email_user_sales_report' => (new UserSalesReport($this->scheduler->company, $data)), + default => $export = false, + }; + + if(!$export) { + $this->cancelSchedule(); + return; + } + $csv = $export->run(); //todo - potentially we send this to more than one user. @@ -72,7 +95,10 @@ class EmailProductSalesReport } - + private function cancelSchedule() + { + $this->scheduler->forceDelete(); + } diff --git a/app/Services/Scheduler/SchedulerService.php b/app/Services/Scheduler/SchedulerService.php index a40f7fd0e4a2..4f34e7b445d3 100644 --- a/app/Services/Scheduler/SchedulerService.php +++ b/app/Services/Scheduler/SchedulerService.php @@ -14,7 +14,7 @@ namespace App\Services\Scheduler; use App\Models\Scheduler; use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesDates; -use App\Services\Scheduler\EmailProductSalesReport; +use App\Services\Scheduler\EmailReport; class SchedulerService { @@ -49,11 +49,15 @@ class SchedulerService (new EmailStatementService($this->scheduler))->run(); } - private function email_product_sales_report() + private function email_report() { - (new EmailProductSalesReport($this->scheduler))->run(); + match($this->scheduler->template){ + 'product_sales_report' => (new EmailReport($this->scheduler))->run(), + default => null + }; } + /** * Sets the next run date of the scheduled task * diff --git a/tests/Feature/Export/TaxSummaryReportTest.php b/tests/Feature/Export/TaxSummaryReportTest.php new file mode 100644 index 000000000000..251a605b4ef0 --- /dev/null +++ b/tests/Feature/Export/TaxSummaryReportTest.php @@ -0,0 +1,203 @@ +faker = \Faker\Factory::create(); + + $this->withoutMiddleware( + ThrottleRequests::class + ); + + $this->withoutExceptionHandling(); + } + + public $company; + + public $user; + + public $payload; + + public $account; + + public $client; + + /** + * start_date - Y-m-d + end_date - Y-m-d + date_range - + all + last7 + last30 + this_month + last_month + this_quarter + last_quarter + this_year + custom + is_income_billed - true = Invoiced || false = Payments + expense_billed - true = Expensed || false = Expenses marked as paid + include_tax - true tax_included || false - tax_excluded + */ + private function buildData() + { + $this->account = Account::factory()->create([ + 'hosted_client_count' => 1000, + 'hosted_company_count' => 1000, + ]); + + $this->account->num_users = 3; + $this->account->save(); + + $this->user = User::factory()->create([ + 'account_id' => $this->account->id, + 'confirmation_code' => 'xyz123', + 'email' => $this->faker->unique()->safeEmail(), + ]); + + $settings = CompanySettings::defaults(); + $settings->client_online_payment_notification = false; + $settings->client_manual_payment_notification = false; + + $this->company = Company::factory()->create([ + 'account_id' => $this->account->id, + 'settings' => $settings, + ]); + + $this->company->settings = $settings; + $this->company->save(); + + $this->payload = [ + 'start_date' => '2000-01-01', + 'end_date' => '2030-01-11', + 'date_range' => 'custom', + 'is_income_billed' => true, + 'include_tax' => false, + ]; + + $this->client = Client::factory()->create([ + 'user_id' => $this->user->id, + 'company_id' => $this->company->id, + 'is_deleted' => 0, + ]); + } + + public function testUserSalesInstance() + { + $this->buildData(); + + $pl = new TaxSummaryReport($this->company, $this->payload); + + $this->assertInstanceOf(TaxSummaryReport::class, $pl); + + $this->account->delete(); + } + + public function testSimpleReport() + { + $this->buildData(); + + + $this->payload = [ + 'start_date' => '2000-01-01', + 'end_date' => '2030-01-11', + 'date_range' => 'custom', + 'client_id' => $this->client->id, + 'report_keys' => [] + ]; + + $i = Invoice::factory()->create([ + 'client_id' => $this->client->id, + 'user_id' => $this->user->id, + 'company_id' => $this->company->id, + 'amount' => 0, + 'balance' => 0, + 'status_id' => 2, + 'total_taxes' => 1, + 'date' => now()->format('Y-m-d'), + 'terms' => 'nada', + 'discount' => 0, + 'tax_rate1' => 10, + 'tax_rate2' => 17.5, + 'tax_rate3' => 5, + 'tax_name1' => 'GST', + 'tax_name2' => 'VAT', + 'tax_name3' => 'CA Sales Tax', + 'uses_inclusive_taxes' => false, + 'line_items' => $this->buildLineItems(), + ]); + + $i = $i->calc()->getInvoice(); + + $pl = new TaxSummaryReport($this->company, $this->payload); + $response = $pl->run(); + + $this->assertIsString($response); + + $this->account->delete(); + } + + + private function buildLineItems() + { + $line_items = []; + + $item = InvoiceItemFactory::create(); + $item->quantity = 1; + $item->cost = 10; + $item->product_key = 'test'; + $item->notes = 'test_product'; + // $item->task_id = $this->encodePrimaryKey($this->task->id); + // $item->expense_id = $this->encodePrimaryKey($this->expense->id); + + $line_items[] = $item; + + + $item = InvoiceItemFactory::create(); + $item->quantity = 1; + $item->cost = 10; + $item->product_key = 'pumpkin'; + $item->notes = 'test_pumpkin'; + // $item->task_id = $this->encodePrimaryKey($this->task->id); + // $item->expense_id = $this->encodePrimaryKey($this->expense->id); + + $line_items[] = $item; + + + return $line_items; + } +} diff --git a/tests/Feature/Scheduler/SchedulerTest.php b/tests/Feature/Scheduler/SchedulerTest.php index fad7c0e76b53..370483683738 100644 --- a/tests/Feature/Scheduler/SchedulerTest.php +++ b/tests/Feature/Scheduler/SchedulerTest.php @@ -25,7 +25,7 @@ use App\DataMapper\Schedule\EmailStatement; use Illuminate\Validation\ValidationException; use Illuminate\Foundation\Testing\WithoutEvents; use App\Services\Scheduler\EmailStatementService; -use App\Services\Scheduler\EmailProductSalesReport; +use App\Services\Scheduler\EmailReport; use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Routing\Middleware\ThrottleRequests; @@ -59,18 +59,48 @@ class SchedulerTest extends TestCase // $this->withoutExceptionHandling(); } + + public function testReportValidationRules() + { + $data = [ + 'name' => 'A test product sales scheduler', + 'frequency_id' => RecurringInvoice::FREQUENCY_MONTHLY, + 'next_run' => now()->format('Y-m-d'), + 'template' => 'email_report', + 'parameters' => [ + 'date_range' => EmailStatement::LAST_MONTH, + 'clients' => [], + 'report_keys' => [], + 'client_id' => $this->client->hashed_id, + 'report_name' => '', + ], + ]; + + $response = false; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->postJson('/api/v1/task_schedulers', $data); + + $response->assertStatus(422); + + } + + public function testProductSalesReportGenerationOneClientSeparateParam() { $data = [ 'name' => 'A test product sales scheduler', 'frequency_id' => RecurringInvoice::FREQUENCY_MONTHLY, 'next_run' => now()->format('Y-m-d'), - 'template' => 'email_product_sales_report', + 'template' => 'email_report', 'parameters' => [ 'date_range' => EmailStatement::LAST_MONTH, 'clients' => [], 'report_keys' => [], 'client_id' => $this->client->hashed_id, + 'report_name' => 'product_sales_report', ], ]; @@ -99,7 +129,7 @@ class SchedulerTest extends TestCase $this->assertNotNull($scheduler); - $export = (new EmailProductSalesReport($scheduler))->run(); + $export = (new EmailReport($scheduler))->run(); $this->assertEquals(now()->addMonth()->format('Y-m-d'), $scheduler->next_run->format('Y-m-d')); @@ -111,13 +141,13 @@ class SchedulerTest extends TestCase 'name' => 'A test product sales scheduler', 'frequency_id' => RecurringInvoice::FREQUENCY_MONTHLY, 'next_run' => now()->format('Y-m-d'), - 'template' => 'email_product_sales_report', + 'template' => 'email_report', 'parameters' => [ 'date_range' => EmailStatement::LAST_MONTH, 'clients' => [$this->client->hashed_id], 'report_keys' => [], 'client_id' => null, - + 'report_name' => 'product_sales_report', ], ]; @@ -145,7 +175,7 @@ class SchedulerTest extends TestCase $this->assertNotNull($scheduler); - $export = (new EmailProductSalesReport($scheduler))->run(); + $export = (new EmailReport($scheduler))->run(); $this->assertEquals(now()->addMonth()->format('Y-m-d'), $scheduler->next_run->format('Y-m-d')); @@ -157,13 +187,13 @@ class SchedulerTest extends TestCase 'name' => 'A test product sales scheduler', 'frequency_id' => RecurringInvoice::FREQUENCY_MONTHLY, 'next_run' => now()->format('Y-m-d'), - 'template' => 'email_product_sales_report', + 'template' => 'email_report', 'parameters' => [ 'date_range' => EmailStatement::LAST_MONTH, 'clients' => [], 'report_keys' => [], 'client_id' => null, - + 'report_name' => 'product_sales_report', ], ]; @@ -188,7 +218,7 @@ class SchedulerTest extends TestCase $this->assertNotNull($scheduler); - $export = (new EmailProductSalesReport($scheduler))->run(); + $export = (new EmailReport($scheduler))->run(); $this->assertEquals(now()->addMonth()->format('Y-m-d'), $scheduler->next_run->format('Y-m-d')); @@ -200,10 +230,11 @@ class SchedulerTest extends TestCase 'name' => 'A test product sales scheduler', 'frequency_id' => RecurringInvoice::FREQUENCY_MONTHLY, 'next_run' => now()->format('Y-m-d'), - 'template' => 'email_product_sales_report', + 'template' => 'email_report', 'parameters' => [ 'date_range' => EmailStatement::LAST_MONTH, 'clients' => [], + 'report_name' => 'product_sales_report', ], ];