crmleaf / salary-templates
Editable Excel templates: salary structure, register, slip and master.
Requires
- php: ^8.2
- crmleaf/payroll-core: ^1.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.58
- orchestra/testbench: ^8.0 || ^9.0 || ^10.0
- phpstan/phpstan: ^1.11 || ^2.0
- phpunit/phpunit: ^10.5 || ^11.0
Suggests
- crmleaf/laravel-payroll: Installs the whole CRMLeaf payroll suite behind one config file, one Blade namespace and one optional JSON API.
- illuminate/support: Only needed for the Laravel service provider, routes and Blade component. The calculator itself is framework-agnostic.
- phpoffice/phpspreadsheet: Required to write the xlsx workbook; the document class throws a clear exception without it.
This package is not auto-updated.
Last update: 2026-08-15 09:37:35 UTC
README
Editable Excel templates: salary structure, register, slip and master.
Generates the four spreadsheets a payroll team actually keeps - salary structure, monthly register, salary slip and employee master - already carrying the statutory columns and the formulae between them.
One of the CRMLeaf payroll tools. The arithmetic
and the dated statutory rate tables live in
crmleaf/payroll-core; this package is
the thin skin that makes one calculator installable, mountable and embeddable on
its own.
Note
A wrong figure or an out-of-date rate is almost always a
payroll-core matter, since
that is where the tables live. Anything about this tool's routes, views or
browser asset belongs here.
Install
Composer - Laravel auto-discovers the service provider, so this is the whole setup:
composer require crmleaf/salary-templates
npm - the same calculation, re-exported from @crmleaf/payroll-js so you can
install this one tool and nothing else:
npm install @crmleaf/salary-templates
Note
Not on npm yet. The script-tag route below needs no registry and works today.
Installing this package straight from git will not resolve
@crmleaf/payroll-js, which is not published yet either.
A plain script tag - no build step, no bundler, no server. Build the browser bundle once and serve the file yourself:
<script src="/js/payroll.min.js"></script> <script> const result = CrmleafPayroll.salaryTemplates({ template: "structure" }); console.log(result.explain); </script>
payroll.min.js is the single-file browser build. Get it by running
npm run build in @crmleaf/payroll-js and copying dist/payroll.min.js
into whatever your site serves as static assets.
A hosted CDN build is coming soon, which will reduce this to a single URL. Serving the file yourself works today and keeps working afterwards - it is the only option that needs no third-party request, so plenty of projects will want to stay on it.
See it working first
demo/index.html in this repository is a working copy of Salary Templates in one file:
the form, the calculation and the working, with no build step and no server. Drop
payroll.min.js beside it and open it from disk.
cp /path/to/payroll-js/dist/payroll.min.js demo/ open demo/index.html
Nothing on that page reaches the network, which is the point: it is a calculator people paste salary figures into.
Use it
Plain PHP, no framework and no container:
use Crmleaf\Payroll\Calculators\SalaryTemplateBuilder; use Crmleaf\Payroll\Money; $result = (new SalaryTemplateBuilder())->calculate( template: 'structure', ); echo $result->explain(); // the formula with the real operands in it echo $result->workings(); // every step, one per line, with its citation print_r($result->toArray()); // snake_case, ready for JSON
Laravel - resolve it from the container, or type-hint it anywhere:
use Crmleaf\Payroll\Calculators\SalaryTemplateBuilder; public function show(SalaryTemplateBuilder $calculator) { return $calculator->calculate( template: 'structure', )->toArray(); }
Blade - one component, no controller:
<x-crmleaf::salary-templates />
HTTP - off by default. Publish the config and turn the route on:
php artisan vendor:publish --tag=salary-templates-config
// config/salary-templates.php 'route' => ['enabled' => true, 'prefix' => 'tools'],
curl -X POST https://example.test/tools/salary-templates \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -d '{"template":"structure"}'
The JSON response carries the figures, the working and the statutory citations:
{
"tool": "salary-templates",
"data": { "…": "every figure, snake_case, with a *_formatted twin" },
"explain": "the formula with the real operands substituted",
"working": [{ "label": "…", "amount": 0, "formula": "…", "citation": "…" }],
"citations": ["…"]
}
JavaScript:
import { salaryTemplates } from '@crmleaf/salary-templates'; const result = salaryTemplates({ template: "structure" });
Documents render inside your application
This tool writes a XLSX file, which means it needs a server. Rendering happens in your application, against your published config, and the bytes never leave your infrastructure - there is no hosted document service and therefore no credential for a browser to carry.
composer require phpoffice/phpspreadsheet
php artisan vendor:publish --tag=salary-templates-config # company name, address, GSTIN, logo
use Crmleaf\Payroll\Tools\SalaryTemplates\Documents\SalaryTemplateBuilderDocument; return app(SalaryTemplateBuilderDocument::class)->download($result, 'salary-templates.xlsx');
Add format=xlsx to the HTTP request and the route returns the
file directly.
Inputs
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
template |
one of structure, register, slip, master |
Yes | "structure" |
|
employee_count |
integer | No | 25 |
|
annual_ctc |
money (₹) | No | 1200000 |
Used to fill the worked example row. |
state |
string | No | "Karnataka" |
|
as_of |
date (YYYY-MM-DD) | No | - |
Optional fields you leave out are omitted from the call entirely, so the calculator's own documented defaults apply.
Every figure here rests on a statutory rate, so the call takes as_of. Set it
and the calculation runs on the rates in force on that date, which is what makes
a prior year recomputable rather than merely rememberable.
Statutory basis
Payment of Wages Act 1936 section 13A and rule 27 of the Code on Wages (Central) Rules 2021 for the register formats; the wage slip particulars of section 13A for the slip layout.
Rates are data, not code: they live in dated tables with a cited source in
crmleaf/payroll-core, so a rate change is a new dated entry rather than an edit
to a constant.
Important
This package implements our reading of the applicable statutes and is provided without warranty. It is a calculation library, not tax advice. Verify against your own compliance obligations before relying on the output for statutory filing.
Publishing
| Tag | Publishes |
|---|---|
salary-templates-config |
config/salary-templates.php |
salary-templates-views |
resources/views/vendor/salary-templates |
salary-templates-assets |
public/vendor/salary-templates |
Licence
MIT © CRMLeaf. Use it commercially, embed it, fork it.