Add salary grade structure for jobs - #1
Merged
Conversation
Introduce a salary grade catalog (SalaryTable, SalaryGrade, SalaryStep) so editors can attach either a tariff-based civil-service pay grade with seniority steps, or a free min/max salary range, to a job posting. This is required by an upcoming law that forbids displaying job postings without salary information; SalaryGrade uses TYPO3's native starttime/endtime so an expired grade automatically makes the referencing job unresolvable via Extbase relation loading. Job's ctrl[type] switches from is_import to salary_mode so the backend can show distinct icons and field sets for grade-based versus free-entry salary jobs. is_import becomes a plain field that now drives a displayCond on vacancy_id instead of driving the record type. This is a breaking change for any project still relying on the previous is_import-based type icon behaviour. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Switch Job's salary_grade from a plain select dropdown to a group field. selectSingle has no search/filter, and once several SalaryTable catalogs exist in parallel, grade titles like "A7" can no longer be told apart. Group gives editors a searchable picker instead. SalaryGrade's own salary_table pointer changes from passthrough to a group relation so its title can be resolved and appended via label_alt/label_alt_force, e.g. "A7, Grundgehaltssaetze Baden- Wuerttemberg" wherever the record title is used (group field, list module). Add a formattedLabel_userFunc for the inline child header inside SalaryTable's own edit form, where that same parent-table suffix would just repeat information the editor already sees. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The label_alt-based title just concatenated the raw step_label and
amount ("3, 3421.84"), which reads more like a typo than a step
number. Prefix it with the step_label field's own label ("Step 3")
and format the amount for the current backend user's locale, joined
by a dash for readability.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Combine title and has_steps into one palette so they sit side by side instead of stacking, saving vertical space in a form that can already hold many inline levels. Also show the "create new" button below the step list in addition to above, and append the foreign table's own title to it, so long lists of steps don't require scrolling back up just to add another one. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Same treatment as the SalaryGrade -> SalaryStep relation: show the "create new" button below the salary grade list in addition to above, with the foreign table's title appended to it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Native <input type="number"> fields strip characters like "," while typing or pasting, so a value copied from a German pay scale table (e.g. "3.421,84") could never be entered correctly - editors had to manually remove the thousands separator first. Add a custom renderType, jobfair2LocalizedDecimal, backed by a JavaScriptModuleInstruction that normalizes the typed value to the machine format on blur, based on where the last "," or "." occurs in the string rather than on the backend user's own UI language - the source data's notation and the editor's language preference are independent of each other. type=>number/format=>decimal is kept unchanged, so the automatic DECIMAL(10,2) column derivation is unaffected. Applied to flat_amount, amount, salary_min and salary_max. Require ext-intl for the NumberFormatter calls this and the SalaryStep title formatter rely on. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Configuration/TCA/*, Configuration/TCA/Overrides/*, RequestMiddlewares.php, Extbase/Persistence/Classes.php and ext_localconf.php referenced classes by their fully qualified name in the code body. Add use imports and reference the short class names instead, per project convention. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
sys_language_uid, l10n_parent, l10n_diffsource, hidden, starttime and endtime were defined manually in all four tables' 'columns' arrays, duplicating exactly what TYPO3 core's TcaEnrichment already generates from the corresponding 'ctrl' settings (languageField, transOrigPointerField, transOrigDiffSourceField, enablecolumns). Move the only real customizations - the legal-notice description on SalaryTable/SalaryGrade's starttime/endtime and the allowLanguageSynchronization behaviour on all four tables - into small Configuration/TCA/Overrides/*.php files, which core applies after enrichment. While at it, fix Job's l10n_source column: the ctrl entry and the actual database column are both named l10n_diffsource, so that 'l10n_source' definition was dead configuration that never matched anything - and add the two missing label translations for SalaryGrade's starttime/endtime, which resolved to blank because their LLL keys never existed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
'exclude' is documented as a boolean; the integer 1 worked by coincidence (PHP truthiness) but did not match the type TCA actually expects. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
'types' array keys must match the actual type of the value the ctrl['type'] pointer field holds. has_steps and salary_mode are int-valued (checkbox / select with integer item values), and tables without a ctrl['type'] resolve to int 0 internally (BackendUtility::getTCAtypeValue()) - so all of them get int keys instead of numeric strings. Also reorder them ascending (SalaryGrade had '1' before '0') and apply the same key typing/order to typeicon_classes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Reorder each table's 'columns' array to follow the same sequence fields actually appear in via showitem/palettes, so the config file reads top-to-bottom the same way the form does. Fields with no showitem placement at all (SalaryGrade's salary_table, SalaryStep's salary_grade - both exist only to resolve the parent title via label_alt, plus Job's non-form fields like link/tender_file/ pdf_files/is_internal/salary_mode) are grouped at the end. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
SalaryStep's salary_grade column just declared type=>passthrough, duplicating exactly what DefaultTcaSchema already generates for a foreign_field pointer column (int(11) NOT NULL DEFAULT 0) when the child table leaves it undefined or passthrough. Drop it from both the TCA columns array and ext_tables.sql. SalaryGrade's own salary_table pointer (the analogous column for SalaryTable's salary_grades relation) is intentionally left as-is: it was changed from passthrough to type=>group earlier so its title can be resolved for label_alt, and DefaultTcaSchema's passthrough fallback does not apply to a group-typed column - its generic default would be a nullable TEXT column instead of the NOT NULL int the relation actually needs, so the manual ext_tables.sql declaration for that one stays. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
type=>group on salary_table let editors jump straight from a SalaryGrade child record into editing its parent SalaryTable via the group field's built-in edit link - not something an inline relation pointer column should ever offer. Revert it to passthrough (its foreign_field default) and resolve the parent's title through a full label_userFunc instead, using BackendUtility::getRecordWSOL() + getRecordTitle() - both already handle workspace overlay internally, so no manual QueryBuilder code is needed for this. Rename InlineRecordTitleFormatter to SalaryGradeTitleFormatter and add formatTitle() (ctrl-level label_userFunc, includes the parent title) alongside the existing formatInlineChildTitle() (IRRE-only, title alone - FormEngine's TcaRecordTitle prioritizes formattedLabel_userFunc over label_userFunc for inline children, so the two never conflict). label_alt/label_alt_force on SalaryGrade are removed, since label_userFunc bypasses that mechanism entirely. The salary_table column itself drops out of ext_tables.sql again - passthrough is DefaultTcaSchema's foreign_field fallback case, so the int(11) column is auto-generated same as SalaryStep's salary_grade. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
TcaPreparation::addSystemFieldsToShowitemTypes() only auto-adds the access tab for tt_content, not for custom extension tables, so the access palette/div still has to be declared explicitly in showitem. However, starttime/endtime already have official core labels (LLL:EXT:frontend...:starttime_formlabel / :endtime_formlabel) and the access palette itself has an official title (LLL:EXT:frontend...:pages.palettes.access), exactly as used by EXT:maps2's poicollection TCA and already used in job.php here. Adopt that native pattern for salarygrade, salarystep and salarytable instead of maintaining redundant custom labels, and drop the now-unused tx_jobfair2_domain_model_salarygrade/salarytable .starttime/.endtime label translations (EN+DE) - the .description keys with the domain-specific legal notice stay untouched.
This extension had no .editorconfig yet, unlike other jweiland/* extensions. Add the standard jweiland.net configuration (4-space indent for PHP, tabs for JSON/XLF/SQL, LF line endings, trimmed trailing whitespace) so editors format new files consistently with the rest of the ecosystem.
- Use the SPDX-preferred "GPL-2.0-or-later" identifier instead of the deprecated "GPL-2.0+" suffix notation. - Prefix the description with "Job fair 2 -" to match ext_emconf.php's title and disambiguate from the original (non-2) jobfair extension. - Alphabetize the require block (ext-intl before typo3/cms-core).
jobfair2 was extracted from Markus Kugler's original project into its own jweiland.net-maintained package (see CLAUDE.md history section). Update author/author_mail to reflect the new maintainer.
Continues the cleanup already applied to salarygrade/salarystep: DefaultTcaSchema derives a matching DB column for every plain input, text, check, datetime, file, select and single-table group field from its TCA config alone, so declaring them again here is redundant boilerplate that has to be kept in sync by hand. Removed: all tx_jobfair2_domain_model_job columns except the two decimal fields, the whole tx_jobfair2_domain_model_jobarea/-jobtype blocks (each only had the auto-generated "title" column), and the tt_address "import_key" column (defined via TCA in Configuration/TCA/Overrides/tt_address.php). Kept: salary_min/salary_max (job), flat_amount (salarygrade) and amount (salarystep) - decimal precision still requires an explicit declaration, TCA alone cannot express it (see Documentation-internal note in the TCA decimal fields). Also reformats the kept decimal(10,2) declarations to decimal(10, 2) for consistent spacing.
Matches the composer.json convention of other jweiland/* extensions, which ship their license text as LICENSE.txt.
Replace the stale require-dev (roave/security-advisories dev-latest, phpunit/phpunit ~4.8.0 - far behind the PHPUnit version shipped by typo3/testing-framework ^13.4) with the actual tooling used by Build/Scripts/runTests.sh: typo3/testing-framework, typo3/coding- standards and ergebnis/composer-normalize. Add autoload-dev for Tests/, and the composer/TYPO3 "config"/"extra" block (.Build vendor- dir, bin-dir, web-dir) the test runner and CI workflow expect.
Standard jweiland.net repo hygiene files: .gitignore excludes the .Build/ test-tooling vendor dir, editor/OS cruft and composer.lock; .gitattributes export-ignores CI/Build/Tests/dotfiles from composer/TER package archives so they don't ship to end users.
Standard TYPO3 core .phpstorm.meta.php: teaches PhpStorm's code completion the concrete return/argument types behind Context::getAspect(), ServerRequestInterface::getAttribute() and similar string-keyed APIs.
- Build/Scripts/runTests.sh: docker/podman-based test runner (cgl,
lint, composer validate/normalize, unit/functional tests,
phpstan), copied from TYPO3's testing-framework template - matches
the suites invoked by the new CI workflow.
- Build/cgl/config.php: php-cs-fixer ruleset per PER-CS1.0/PSR-12.
- Build/phpunit/{Unit,Functional}Tests.xml + bootstrap files: minimal
PHPUnit setup for Tests/Unit and Tests/Functional (both still to be
created).
Fixed a leftover "jweiland/events2" package reference (copy-paste
from another jweiland extension) in the header comments of
UnitTests.xml, UnitTestsBootstrap.php and FunctionalTestsBootstrap.php
to correctly say "jweiland/jobfair2".
- ci.yml: runs Build/Scripts/runTests.sh (lint, composer validate/normalize, cgl) on PHP 8.2/8.3 for every pull request. - backport.yml: opens backport PRs on merge, driven by a "Backport <branch>" label. - ter-release.yml: publishes to the TYPO3 Extension Repository via typo3/tailor whenever a GitHub release is published.
The CI workflow added in b69e54e runs Build/Scripts/runTests.sh -s cgl in dry-run mode, which failed on PR #1 (25 of 54 files) because the existing codebase predates the CGL ruleset that ships with this same branch (Build/cgl/config.php). Applied `php-cs-fixer fix` with that exact config: missing package header comments, trailing commas in multiline argument/parameter lists, brace position and single-line empty body style. No behavioral changes.
runTests.sh auto-detects podman before docker when both are installed. GitHub's ubuntu-latest runners ship both, but their current podman/crun combination fails on every container pull with "OCI runtime error: crun: unknown version specified", so every step (lint, composerUpdate, composerValidate, composerNormalize, cgl) failed regardless of PHP version - reproduced twice, not a one-off flake. Pass -b docker explicitly to bypass the broken podman path.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
SalaryTable->SalaryGrade->SalaryStep) so a Job's salary can reference a maintained pay-scale grade instead of only free-text min/max values, including a locale-aware decimal input (3.842,17) for all amount fields.sys_language_uid,hidden,starttime/endtime, ...) removed fromcolumnsin favor of core'sTcaEnrichment,types/typeicon_classeskeys typed and sorted correctly,columnsorder aligned withshowitem, redundantforeign_field/ext_tables.sqlboilerplate removed whereverDefaultTcaSchemaalready derives it, and theaccesstab switched to TYPO3's own native labels instead of custom translations..editorconfig,.gitignore/.gitattributes,.phpstorm.meta.php,Build/(runTests.sh, CGL config, PHPUnit bootstrap/config) and GitHub Actions workflows (CI, backport, TER release).composer.json(SPDX license id, description, require-dev),ext_emconf.phpauthor,LICENSErenamed toLICENSE.txt.Test plan
php -l) and XLF files validated (xmllint, EN/DE trans-unit counts matched).composer validatepasses.ci.yml) has not run yet in this repo - first PR to exercise it.