Adds comprehensive financial management capabilities: - Accounts Receivable (AR) with customers, invoices, payments - Budget management with lines and variance tracking - Expense tracking with approval workflow - Fixed asset management with depreciation - Recurring transaction schedules (AP, AR, journal entries) - CFO dashboard with financial KPIs - Cash flow forecasting - Directory views for customers and vendors - Division filtering support across all modules
104 lines
3.3 KiB
PHP
104 lines
3.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Services\Accounting\RecurringSchedulerService;
|
|
use Carbon\Carbon;
|
|
use Illuminate\Console\Command;
|
|
|
|
class RunRecurringSchedules extends Command
|
|
{
|
|
protected $signature = 'recurring:run
|
|
{--date= : The date to run schedules for (YYYY-MM-DD, default: today)}
|
|
{--business= : Specific business ID to run schedules for}
|
|
{--dry-run : Preview what would be generated without actually creating transactions}';
|
|
|
|
protected $description = 'Run due recurring schedules to generate AR invoices, AP bills, and journal entries';
|
|
|
|
public function __construct(
|
|
protected RecurringSchedulerService $schedulerService
|
|
) {
|
|
parent::__construct();
|
|
}
|
|
|
|
public function handle(): int
|
|
{
|
|
$dateString = $this->option('date');
|
|
$businessId = $this->option('business') ? (int) $this->option('business') : null;
|
|
$dryRun = $this->option('dry-run');
|
|
|
|
$date = $dateString ? Carbon::parse($dateString) : now();
|
|
|
|
$this->info("Running recurring schedules for {$date->toDateString()}...");
|
|
|
|
if ($businessId) {
|
|
$this->info("Filtering to business ID: {$businessId}");
|
|
}
|
|
|
|
// Get due schedules
|
|
$dueSchedules = $this->schedulerService->getDueSchedules($date, $businessId);
|
|
|
|
if ($dueSchedules->isEmpty()) {
|
|
$this->info('No schedules are due for execution.');
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
$this->info("Found {$dueSchedules->count()} schedule(s) due for execution.");
|
|
|
|
if ($dryRun) {
|
|
$this->warn('DRY RUN MODE - No transactions will be created.');
|
|
$this->table(
|
|
['ID', 'Name', 'Type', 'Business', 'Next Run Date', 'Auto Post'],
|
|
$dueSchedules->map(fn ($s) => [
|
|
$s->id,
|
|
$s->name,
|
|
$s->type_label,
|
|
$s->business->name ?? 'N/A',
|
|
$s->next_run_date->toDateString(),
|
|
$s->auto_post ? 'Yes' : 'No',
|
|
])
|
|
);
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
// Run all due schedules
|
|
$results = $this->schedulerService->runAllDue($date, $businessId);
|
|
|
|
// Output results
|
|
$this->newLine();
|
|
$this->info('Execution Summary:');
|
|
$this->line(" Processed: {$results['processed']}");
|
|
$this->line(" Successful: {$results['success']}");
|
|
$this->line(" Failed: {$results['failed']}");
|
|
|
|
if (! empty($results['generated'])) {
|
|
$this->newLine();
|
|
$this->info('Generated Transactions:');
|
|
$this->table(
|
|
['Schedule', 'Type', 'Result ID'],
|
|
collect($results['generated'])->map(fn ($g) => [
|
|
$g['schedule_name'],
|
|
$g['type'],
|
|
$g['result_id'],
|
|
])
|
|
);
|
|
}
|
|
|
|
if (! empty($results['errors'])) {
|
|
$this->newLine();
|
|
$this->error('Errors:');
|
|
foreach ($results['errors'] as $error) {
|
|
$this->line(" [{$error['schedule_id']}] {$error['schedule_name']}: {$error['error']}");
|
|
}
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
}
|