diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 10cb1bb3f9..d9bfd1342f 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -12,6 +12,7 @@ namespace App\Console; use App\Jobs\Cron\AutoBillCron; +use App\Jobs\Cron\RecurringExpensesCron; use App\Jobs\Cron\RecurringInvoicesCron; use App\Jobs\Cron\SubscriptionCron; use App\Jobs\Ninja\AdjustEmailQuota; @@ -61,7 +62,9 @@ class Kernel extends ConsoleKernel $schedule->job(new SubscriptionCron)->daily()->withoutOverlapping(); $schedule->job(new RecurringInvoicesCron)->hourly()->withoutOverlapping(); - + + $schedule->job(new RecurringExpensesCron)->dailyAt('23:45')->withoutOverlapping(); + $schedule->job(new AutoBillCron)->dailyAt('00:30')->withoutOverlapping(); $schedule->job(new SchedulerCheck)->daily()->withoutOverlapping(); diff --git a/app/Factory/RecurringExpenseFactory.php b/app/Factory/RecurringExpenseFactory.php index 08562266f9..bf72ac0997 100644 --- a/app/Factory/RecurringExpenseFactory.php +++ b/app/Factory/RecurringExpenseFactory.php @@ -21,6 +21,7 @@ class RecurringExpenseFactory $recurring_expense->user_id = $user_id; $recurring_expense->company_id = $company_id; $recurring_expense->is_deleted = false; + $recurring_expense->invoice_documents = false; $recurring_expense->should_be_invoiced = false; $recurring_expense->tax_name1 = ''; $recurring_expense->tax_rate1 = 0; @@ -28,6 +29,9 @@ class RecurringExpenseFactory $recurring_expense->tax_rate2 = 0; $recurring_expense->tax_name3 = ''; $recurring_expense->tax_rate3 = 0; + $recurring_expense->tax_amount1 = 0; + $recurring_expense->tax_amount2 = 0; + $recurring_expense->tax_amount3 = 0; $recurring_expense->date = null; $recurring_expense->payment_date = null; $recurring_expense->amount = 0; @@ -39,6 +43,8 @@ class RecurringExpenseFactory $recurring_expense->custom_value2 = ''; $recurring_expense->custom_value3 = ''; $recurring_expense->custom_value4 = ''; + $recurring_expense->uses_inclusive_taxes = true; + $recurring_expense->calculate_tax_by_amount = true; return $recurring_expense; } diff --git a/app/Factory/RecurringExpenseToExpenseFactory.php b/app/Factory/RecurringExpenseToExpenseFactory.php new file mode 100644 index 0000000000..76a3b0456e --- /dev/null +++ b/app/Factory/RecurringExpenseToExpenseFactory.php @@ -0,0 +1,62 @@ +user_id = $recurring_expense->user_id; + $expense->assigned_user_id = $recurring_expense->assigned_user_id; + $expense->vendor_id = $recurring_expense->vendor_id; + $expense->invoice_id = $recurring_expense->invoice_id; + $expense->currency_id = $recurring_expense->currency_id; + $expense->company_id = $recurring_expense->company_id; + $expense->bank_id = $recurring_expense->bank_id; + $expense->exchange_rate = $recurring_expense->exchange_rate; + $expense->is_deleted = false; + $expense->should_be_invoiced = $recurring_expense->should_be_invoiced; + $expense->tax_name1 = $recurring_expense->tax_name1; + $expense->tax_rate1 = $recurring_expense->tax_rate1; + $expense->tax_name2 = $recurring_expense->tax_name2; + $expense->tax_rate2 = $recurring_expense->tax_rate2; + $expense->tax_name3 = $recurring_expense->tax_name3; + $expense->tax_rate3 = $recurring_expense->tax_rate3; + $expense->date = now()->format('Y-m-d'); + $expense->payment_date = $recurring_expense->payment_date; + $expense->amount = $recurring_expense->amount; + $expense->foreign_amount = $recurring_expense->foreign_amount; + $expense->private_notes = $recurring_expense->private_notes; + $expense->public_notes = $recurring_expense->public_notes; + $expense->transaction_reference = $recurring_expense->transaction_reference; + $expense->custom_value1 = $recurring_expense->custom_value1; + $expense->custom_value2 = $recurring_expense->custom_value2; + $expense->custom_value3 = $recurring_expense->custom_value3; + $expense->custom_value4 = $recurring_expense->custom_value4; + $expense->transaction_id = $recurring_expense->transaction_id; + $expense->category_id = $recurring_expense->category_id; + $expense->payment_type_id = $recurring_expense->payment_type_id; + $expense->project_id = $recurring_expense->project_id; + $expense->invoice_documents = $recurring_expense->invoice_documents; + $expense->tax_amount1 = $recurring_expense->tax_amount1; + $expense->tax_amount2 = $recurring_expense->tax_amount2; + $expense->tax_amount3 = $recurring_expense->tax_amount3; + $expense->uses_inclusive_taxes = $recurring_expense->uses_inclusive_taxes; + $expense->calculate_tax_by_amount = $recurring_expense->calculate_tax_by_amount; + + return $expense; + } +} diff --git a/app/Jobs/Cron/RecurringExpensesCron.php b/app/Jobs/Cron/RecurringExpensesCron.php new file mode 100644 index 0000000000..a08f4eeedb --- /dev/null +++ b/app/Jobs/Cron/RecurringExpensesCron.php @@ -0,0 +1,95 @@ +format('Y-m-d h:i:s')); + + if (! config('ninja.db.multi_db_enabled')) { + + $this->getRecurringExpenses(); + + } else { + //multiDB environment, need to + foreach (MultiDB::$dbs as $db) { + MultiDB::setDB($db); + + $this->getRecurringExpenses(); + } + } + } + + private function getRecurringExpenses() + { + $recurring_expenses = RecurringExpense::where('next_send_date', '<=', now()->toDateTimeString()) + ->whereNotNull('next_send_date') + ->whereNull('deleted_at') + ->where('remaining_cycles', '!=', '0') + // ->whereHas('client', function ($query) { + // $query->where('is_deleted',0) + // ->where('deleted_at', NULL); + // }) + ->with('company') + ->cursor(); + + nlog(now()->format('Y-m-d') . ' Generating Recurring Expenses. Count = '.$recurring_expenses->count()); + + $recurring_expenses->each(function ($recurring_expense, $key) { + nlog("Current date = " . now()->format("Y-m-d") . " Recurring date = " .$recurring_expense->next_send_date); + + if (!$recurring_expense->company->is_disabled) { + $this->generateExpense($recurring_expense); + } + }); + } + + private function generateExpense(RecurringExpense $recurring_expense) + { + $expense = RecurringExpenseToExpenseFactory::create($recurring_expense); + $expense->save(); + + $expense->number = $this->getNextExpenseNumber($expense); + $expense->save(); + } + +} diff --git a/database/migrations/2021_08_23_101529_recurring_expenses_schema.php b/database/migrations/2021_08_23_101529_recurring_expenses_schema.php index 19d43dd3b4..42d90a8d69 100644 --- a/database/migrations/2021_08_23_101529_recurring_expenses_schema.php +++ b/database/migrations/2021_08_23_101529_recurring_expenses_schema.php @@ -37,7 +37,7 @@ class RecurringExpensesSchema extends Migration $table->date('date')->nullable(); $table->date('payment_date')->nullable(); $table->boolean('should_be_invoiced')->default(false); - $table->boolean('invoice_documents')->default(); + $table->boolean('invoice_documents')->default(false); $table->string('transaction_id')->nullable(); $table->string('custom_value1')->nullable(); $table->string('custom_value2')->nullable(); @@ -46,14 +46,14 @@ class RecurringExpensesSchema extends Migration $table->unsignedInteger('category_id')->nullable(); $table->boolean('calculate_tax_by_amount')->default(false); - $table->decimal('tax_amount1', 20, 6); - $table->decimal('tax_amount2', 20, 6); - $table->decimal('tax_amount3', 20, 6); - $table->decimal('tax_rate1', 20, 6); - $table->decimal('tax_rate2', 20, 6); - $table->decimal('tax_rate3', 20, 6); - $table->decimal('amount', 20, 6); - $table->decimal('foreign_amount', 20, 6); + $table->decimal('tax_amount1', 20, 6)->nullable(); + $table->decimal('tax_amount2', 20, 6)->nullable(); + $table->decimal('tax_amount3', 20, 6)->nullable(); + $table->decimal('tax_rate1', 20, 6)->nullable(); + $table->decimal('tax_rate2', 20, 6)->nullable(); + $table->decimal('tax_rate3', 20, 6)->nullable(); + $table->decimal('amount', 20, 6)->nullable(); + $table->decimal('foreign_amount', 20, 6)->nullable(); $table->decimal('exchange_rate', 20, 6)->default(1); $table->unsignedInteger('assigned_user_id')->nullable(); $table->string('number')->nullable(); diff --git a/tests/Unit/RecurringExpenseCloneTest.php b/tests/Unit/RecurringExpenseCloneTest.php new file mode 100644 index 0000000000..6dee502b11 --- /dev/null +++ b/tests/Unit/RecurringExpenseCloneTest.php @@ -0,0 +1,69 @@ +faker = \Faker\Factory::create(); + + } + + public function testBadBase64String() + { + $account = Account::factory()->create(); + $user = User::factory()->create(['account_id' => $account->id, 'email' => $this->faker->unique()->safeEmail]); + $company = Company::factory()->create(['account_id' => $account->id]); + + $client = Client::factory()->create([ + 'user_id' => $user->id, + 'company_id' => $company->id, + ]); + + $recurring_expense = RecurringExpenseFactory::create($company->id, $user->id); + $recurring_expense->date = now(); + $recurring_expense->amount = 10; + $recurring_expense->foreign_amount = 20; + $recurring_expense->exchange_rate = 0.5; + $recurring_expense->private_notes = "private"; + $recurring_expense->public_notes = "public"; + $recurring_expense->custom_value4 = "custom4"; + $recurring_expense->should_be_invoiced = true; + + $recurring_expense->save(); + + $expense = RecurringExpenseToExpenseFactory::create($recurring_expense); + $expense->save(); + + $this->assertNotNull($expense); + $this->assertEquals(20, $expense->foreign_amount); + $this->assertEquals(10, $expense->amount); + } + +} \ No newline at end of file