ezesha/end-year-reports
Composer 安装命令:
composer require ezesha/end-year-reports
包简介
Asynchronous end-year financial report generation module for CodeIgniter 4 (SQS-backed worker pipeline).
README 文档
README
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
- PHP
^8.1 - CodeIgniter 4
^4.4 aws/aws-sdk-php^3.0(pulled in automatically)- An SQS queue — real AWS, or LocalStack for local dev
codeigniter4/tasks(optional) — only if you want the bundled scheduled workers
Installation
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::$discoverInComposermust staytrue, which is the default).
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
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
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
# 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
| Route | Purpose |
|---|---|
GET /end_year_report |
The dashboard web page |
GET /api/reports/office/{officeId}/{date} |
Enqueue + poll; returns PROCESSING/READY/FAILED |
| Command | Purpose |
|---|---|
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
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 underdata.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 & extension
Everything tunable lives in
Ezesha\EndYearReports\Config\EndYearReports.
Override it by publishing an App\Config\EndYearReports class (same short name)
in the host app — CodeIgniter's Factories resolve the app namespace first.
| Property | Default | Meaning |
|---|---|---|
$dataProvider |
MockFinancialDataProvider |
Class that supplies the raw financial package |
$pipeline |
trial → income → balance | Ordered stageKey => serviceClass map |
$staleThresholdSeconds |
240 |
When a pending job is treated as stalled/failed |
$sqsUrl / $aws* |
env‑driven | Connection settings (host .env overrides at construct time) |
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:
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)
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
Table end_year_report (see the migration):
- Unique key
unique_office_closureon(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 istrial_balance; the code readstrail_balance ?? trial_balanceand the model allows both.
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
MIT — see LICENSE.
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 2
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-07-07