Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

GeorgeKala's avatar

Cron Jobs

Hello, I have this situation, I have an organization model that is in relationship with an invoice, the organization has a payment date 5 days before this date, an invoice should be generated and sent to the organization by email, I did this with one job, then I used this job in the artisan command, I wonder if I should write this logic better, if I have it in the command, what is the difference.

public function handle() { $today = now(); $year = $today->year; $month = $today->month;

    $organizations = BuyerOrganization::whereNotNull('payment_date')
        ->whereRaw("
    CASE
        WHEN payment_date <= extract(day FROM make_date(?, ?, 1) + interval '1 month - 1 day') THEN
            (make_date(?, ?, payment_date) - ?::date) = ?
        ELSE false
    END
    ", [$year, $month, $year, $month, $today->toDateString(), 5])
        ->get();

    if ($organizations->isEmpty()) {
        $this->info('No organizations found for invoice generation.');

        return;
    }

    foreach ($organizations as $organization) {
        GenerateInvoiceJob::dispatch($organization);
    }

    $this->info('Invoice generation jobs dispatched successfully.');
}
0 likes
1 reply
deantedesco's avatar

You have separated your concerns here to a degree, it is good that you have a job which seems to handle one Invoice for an organisation (GenerateInvoiceJob), this seems like a logical boundary for your code. As for what you have in your console command, while it is perfectly fine, you could optimise that some more and potentially abstract your query away from your command into a repository or maybe even a scope (not sure if that will work but worth a look).

The other thing I would suggest is if this is to be added to the apps scheduler, then consider making this into a parent Job and schedule this instead as it may never be run as a command outside of the scheduler and that seems like a waste of a command IMO. If you are making the command for testing purposes it is just as easy to use tinker to dispatch a job.

The last thing I'll mention is to make sure the GenerateInvoiceJob runs off your queue as this can help you to:

  1. Track the invoices generated (especially if using Horizon)
  2. Reduce the load on your main server if you run your queue off a worker instance.
  3. Allow you to see if jobs (invoices) have failed to send and allow you to resend the failed ones once you figure out the issue.

Hopefully this helps.

Please or to participate in this conversation.