PHPackages                             ezesha/end-year-reports - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Queues &amp; Workers](/categories/queues)
4. /
5. ezesha/end-year-reports

ActiveLibrary[Queues &amp; Workers](/categories/queues)

ezesha/end-year-reports
=======================

Asynchronous end-year financial report generation module for CodeIgniter 4 (SQS-backed worker pipeline).

v1.0.4(2d ago)06MITPHP ^8.1

Since Jul 8Compare

[ Source](https://github.com/nkarisa/end-year-report)[ Packagist](https://packagist.org/packages/ezesha/end-year-reports)[ RSS](/packages/ezesha-end-year-reports/feed)WikiDiscussions Synced today

READMEChangelogDependencies (2)Versions (6)Used By (0)

ezesha/end-year-reports
=======================

[](#ezeshaend-year-reports)

Asynchronous **end‑year financial report generation** for CodeIgniter 4.

A user picks an office and a closure date and clicks **Generate Package**; the HTTP request enqueues the job on **Amazon SQS** (LocalStack‑friendly) and returns immediately, a background worker runs an ordered pipeline (Trial Balance → Income Statement → Balance Sheet), writes the results back, and the browser polls until the package is `READY`. Failed jobs self‑heal via a retry command.

This module is a self‑contained CodeIgniter 4 package: controllers, worker commands, model, migration, services, views and frontend assets, all under the `Ezesha\EndYearReports\` namespace and auto‑discovered by the framework.

---

Requirements
------------

[](#requirements)

- PHP `^8.1`
- CodeIgniter 4 `^4.4`
- `aws/aws-sdk-php` `^3.0` (pulled in automatically)
- An SQS queue — real AWS, or [LocalStack](https://localstack.cloud/) for local dev
- `codeigniter4/tasks` *(optional)* — only if you want the bundled scheduled workers

---

Installation
------------

[](#installation)

### 1. Require the package

[](#1-require-the-package)

Published to a Git host / Packagist:

```
composer require ezesha/end-year-reports
```

Or, while developing it locally, add a **path repository** to the application's `composer.json` and require it:

```
{
    "repositories": [
        { "type": "path", "url": "../packages/end-year-reports" }
    ],
    "require": {
        "ezesha/end-year-reports": "*"
    }
}
```

```
composer update ezesha/end-year-reports
```

> CodeIgniter auto‑discovers the module's **routes, commands, migrations, services and config** as soon as Composer registers the namespace — no manual wiring in `App\Config\*` is required (`Config\Modules::$discoverInComposer`must stay `true`, which is the default).

### 2. Configure the environment

[](#2-configure-the-environment)

Add to the application `.env`:

```
# SQS / AWS
sqs_url                 = "http://sqs.eu-west-1.localhost.localstack.cloud:4566/000000000000/generate-reports"
aws.region              = "eu-west-1"
aws.credentials_profile = "default"
aws.endpoint            = "http://localhost:4566"   # LocalStack; drop for real AWS

# Optional — real AWS credentials (default to mock values for LocalStack)
# aws.access_key_id     = "AKIA..."
# aws.secret_access_key = "..."
```

### 3. Run the migration

[](#3-run-the-migration)

The migration lives in the package namespace, so run migrations for **all**namespaces (or target this one):

```
php spark migrate --all
# or
php spark migrate -n "Ezesha\EndYearReports"
```

### 4. Publish the frontend assets

[](#4-publish-the-frontend-assets)

Copies the module's JS and CSS into the app's `public/` directory:

```
php spark endyearreports:publish
```

This writes `public/ezesha/js/end_year_report/*` and `public/ezesha/styles/end_year_report.css`, which the shipped view references at `/ezesha/js/...` and `/ezesha/styles/...`. The `ezesha/` sub-folder namespaces the assets so they never collide with the host app's own `public/js` or `public/styles`.

### 5. Run the worker

[](#5-run-the-worker)

```
# Drain the queue once (good for a cron tick / supervisor loop)
php spark workers:process-reports --once

# Or drain a batch and exit early when empty
php spark workers:process-reports --max-messages 50

# Or run as a long-lived daemon (10s long-poll)
php spark workers:process-reports
```

Open **`/end_year_report`** in the browser, choose an office and closure date, and click **Generate Package**.

---

Usage summary
-------------

[](#usage-summary)

RoutePurpose`GET /end_year_report`The dashboard web page`GET /api/reports/office/{officeId}/{date}`Enqueue + poll; returns `PROCESSING`/`READY`/`FAILED`CommandPurpose`workers:process-reports [--once] [--max-messages N]`Consume the queue and run the compilation pipeline`workers:retry-failed [--max-age-hours N] [--dry-run]`Re‑queue reports stuck in the failed state`endyearreports:publish`Publish frontend assets into `public/ezesha/`### API states

[](#api-states)

The API is **idempotent per `(office_id, closure_date)`** (enforced by a unique key) and returns a `queue_state`:

- `PROCESSING` (`202`) — queued / in flight; the frontend polls every 5s.
- `READY` (`200`) — compiled; response carries the three statements under `data`.
- `FAILED` (`500`) — pipeline rolled back, or the job stalled past the threshold.

A user "Generate" click sends `?retry=1` to clear a previous failure and re‑run; passive polls omit it so genuine failures surface instead of looping.

---

Configuration &amp; extension
-----------------------------

[](#configuration--extension)

Everything tunable lives in [`Ezesha\EndYearReports\Config\EndYearReports`](src/Config/EndYearReports.php). Override it by publishing an `App\Config\EndYearReports` class (same short name) in the host app — CodeIgniter's Factories resolve the app namespace first.

PropertyDefaultMeaning`$dataProvider``MockFinancialDataProvider`Class that supplies the raw financial package`$pipeline`trial → income → balanceOrdered `stageKey => serviceClass` map`$staleThresholdSeconds``240`When a pending job is treated as stalled/failed`$sqsUrl` / `$aws*`env‑drivenConnection settings (host `.env` overrides at construct time)### Plugging in real data (the important one)

[](#plugging-in-real-data-the-important-one)

The shipped `MockFinancialDataProvider` returns a static fixture so the module runs out of the box. To feed **real ledger data**, implement [`FinancialDataProviderInterface`](src/DataProviders/FinancialDataProviderInterface.php):

```
namespace App\Reports;

use Ezesha\EndYearReports\DataProviders\FinancialDataProviderInterface;

class LedgerDataProvider implements FinancialDataProviderInterface
{
    public function getPackage(int $officeId, string $closureDate): array
    {
        // Query your ledger for this office / period and return:
        return [
            'trial_balance'    => [ /* accounts, totals */ ],
            'income_statement' => [ /* revenues, expenses, ... */ ],
            'balance_sheet'    => [ /* assets, liabilities, equity */ ],
        ];
    }
}
```

Then point config at it (via your `App\Config\EndYearReports`):

```
public string $dataProvider = \App\Reports\LedgerDataProvider::class;
```

The pipeline services resolve the provider through `service('financialDataProvider')`, so no other change is needed.

---

Scheduling (optional, requires `codeigniter4/tasks`)
----------------------------------------------------

[](#scheduling-optional-requires-codeigniter4tasks)

The package does not force a scheduler on you. To run the workers automatically, add these to the host app's `App\Config\Tasks::init()`:

```
$schedule->command('workers:process-reports --max-messages 50')
         ->everyMinute()
         ->named('process-reports')
         ->singleInstance();

$schedule->command('workers:retry-failed --max-age-hours 24')
         ->everyFiveMinutes()
         ->named('retry-failed-reports');
```

…and wire the CodeIgniter task runner into system cron:

```
* * * * * cd /path/to/app && php spark tasks:run >> /dev/null 2>&1
```

> **Why bounded worker runs?** A long‑lived PHP process caches every class in memory, so it keeps running *stale* code after you edit a service. Running a fresh, bounded process each tick (`--max-messages`) always loads current code.

---

Data model
----------

[](#data-model)

Table `end_year_report` (see the [migration](src/Database/Migrations/2026-07-07-112116_CreateEndYearReportTable.php)):

- Unique key `unique_office_closure` on `(fk_office_id, end_year_report_closure_date)`blocks duplicate placeholders under concurrency.
- `end_year_report_is_valid`: `0` = pending, `1` = valid/ready, `2` = failed/rolled back.
- The JSON column is physically named **`trail_balance`** (historical spelling) while the pipeline/API key is `trial_balance`; the code reads `trail_balance ?? trial_balance` and the model allows both.

---

Package layout
--------------

[](#package-layout)

```
src/
├── Config/
│   ├── EndYearReports.php     # tunable config (data provider, pipeline, SQS)
│   ├── Services.php           # service('financialDataProvider' | 'endYearReportsConfig')
│   └── Routes.php             # auto-discovered module routes
├── Commands/
│   ├── ProcessReportQueue.php # workers:process-reports
│   ├── RetryFailedReports.php # workers:retry-failed
│   └── PublishAssets.php      # endyearreports:publish
├── Controllers/
│   ├── EndYearReport.php      # GET /end_year_report
│   └── Api/FinancialReportController.php
├── Services/FinancialReports/ # pipeline interface + 3 stage services
├── DataProviders/             # FinancialDataProviderInterface + mock default
├── Models/EndYearReportModel.php
├── Database/Migrations/
├── Support/Sqs.php            # shared SQS client / queue-url factory
└── Views/end_year_report.php
assets/                        # frontend JS + CSS (published to public/)

```

License
-------

[](#license)

MIT — see [LICENSE](LICENSE).

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance99

Actively maintained with recent releases

Popularity6

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity46

Maturing project, gaining track record

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

5

Last Release

2d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/b6f24e4d3ba3be0ee9438a2f381357b00fd868eba9fb3a60492e914b1bdfc55a?d=identicon)[nkmwambs](/maintainers/nkmwambs)

---

Tags

codeigniterqueuesqsworkercodeigniter4financial-reportsend-year

### Embed Badge

![Health badge](/badges/ezesha-end-year-reports/health.svg)

```
[![Health](https://phpackages.com/badges/ezesha-end-year-reports/health.svg)](https://phpackages.com/packages/ezesha-end-year-reports)
```

###  Alternatives

[dusterio/laravel-aws-worker

Run Laravel (or Lumen) tasks and queue listeners inside of AWS Elastic Beanstalk workers

3115.9M](/packages/dusterio-laravel-aws-worker)[shiftonelabs/laravel-sqs-fifo-queue

Adds a Laravel queue driver for Amazon SQS FIFO queues.

1556.7M4](/packages/shiftonelabs-laravel-sqs-fifo-queue)[enqueue/sqs

Message Queue Amazon SQS Transport

376.6M20](/packages/enqueue-sqs)[yidas/codeigniter-queue-worker

CodeIgniter 3 Queue Worker Management Controller

9666.9k2](/packages/yidas-codeigniter-queue-worker)[joblocal/laravel-sqs-sns-subscription-queue

A simple Laravel service provider which adds a new queue connector to handle SNS subscription queues.

48459.0k](/packages/joblocal-laravel-sqs-sns-subscription-queue)[atymic/laravel-bulk-sqs-queue

Laravel SQS Bulk Queue

15196.7k](/packages/atymic-laravel-bulk-sqs-queue)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
