# EasyHR Billing server cron

The server needs one cron entry. It calls Laravel every minute. Laravel then decides when to generate invoices.

`billing:generate-monthly` is scheduled for **23:50 Asia/Dhaka every day**. The command itself creates invoices only on the **last calendar day** of that month. On every other day it exits with `Skipped. Today is not the last day of the month.`

No queue worker is required. The app queue is `sync`.

## 1. Confirm PHP and the schedule

SSH into the billing server, then run these from the EasyHR Billing directory:

```bash
which php
php artisan schedule:list
```

`schedule:list` should show:

```text
50 23 * * * php artisan billing:generate-monthly
```

That line is 23:50 in `Asia/Dhaka`, because the schedule sets that timezone.

## 2. Linux cron (production)

Edit the crontab for the same user that owns the site files (often `www-data`):

```bash
sudo crontab -u www-data -e
```

Add one line. Replace the directory and the PHP path from `which php`:

```cron
* * * * * cd /var/www/EasyHR_Billing && /usr/bin/php artisan schedule:run >> storage/logs/scheduler.log 2>&1
```

Save, then confirm it is installed:

```bash
sudo crontab -u www-data -l
```

What this does:

- Every minute, cron runs `php artisan schedule:run`.
- At 23:50 Asia/Dhaka, Laravel runs `billing:generate-monthly`.
- On the last day of the month, that command creates the current month’s invoices and syncs them to each company site.
- On other days, the command skips. A line in `storage/logs/scheduler.log` is expected.

The directory must be writable by that user, especially `storage/` and `bootstrap/cache/`.

## 3. Windows server (Task Scheduler)

Use this when EasyHR Billing is hosted on Windows, including Laragon.

1. Open **Task Scheduler** and choose **Create Task**.
2. General: name it `EasyHR Billing schedule`. Select **Run whether user is logged on or not**.
3. Triggers: **New** → **Daily** → repeat task every **1 minute** for a duration of **1 day**. Check **Stop task if it runs longer than** 5 minutes.
4. Actions: **Start a program**.
   - Program: the full path to `php.exe` (Laragon example: `D:\laragon\bin\php\php-8.3.16-Win32-vs16-x64\php.exe`).
   - Arguments: `artisan schedule:run`
   - Start in: `D:\laragon\www\EasyHR_Billing`
5. Conditions: uncheck **Start the task only if the computer is on AC power** if this is a desktop.

Save the task and run it once from Task Scheduler to confirm it finishes.

## 4. Check that it is working

Run the scheduler once by hand:

```bash
php artisan schedule:run
```

Before 23:50 it prints `No scheduled commands are ready to run.` That is correct.

To see the billing command without waiting for month-end:

```bash
php artisan billing:generate-monthly
```

Before the last day this prints `Skipped. Today is not the last day of the month.` and does not create invoices.

After the cron has been in place for a few minutes, `storage/logs/scheduler.log` should show those same lines around 23:50. `storage/logs/laravel.log` should stay free of scheduler errors.

If a month was missed, generate it from the dashboard (**Generate a month**) or:

```bash
php artisan billing:generate-monthly --month=2026-10
```

`--month` creates that month even when today is not the last day. It still skips a company that already has an invoice for that month.
