* Add SI-prefix-aware sorting column for the parts table
Adds an optional "Name (SI)" column that parses numeric values with SI
prefixes (p, n, u/µ, m, k/K, M, G, T) from part names and sorts by the
resulting physical value. This is useful for electronic components where
alphabetical sorting produces wrong results — e.g. 100nF, 10pF, 1uF
should sort as 10pF < 100nF < 1uF.
Implementation:
- New SiValueSort DQL function with platform-specific SQL generation
for PostgreSQL (POSIX regex), MySQL/MariaDB (REGEXP_SUBSTR), and
SQLite (PHP callback registered via the existing middleware).
- The regex is start-anchored: only names beginning with a number are
matched. Part numbers like "MCP2515" or "Crystal 20MHz" are ignored.
- When SI sort is active, NATSORT is appended as a secondary sort so
that non-matching parts fall back to natural string ordering instead
of appearing in arbitrary order.
- The column is opt-in (not in default columns) and displays the parsed
float value, or an empty cell for non-matching names.
* Rename SI column from "Name (SI)" to "SI Value"
The column now shows the parsed numeric value rather than the part name,
so the label should reflect that.
* Support comma as decimal separator in SI value parsing
Part names using European decimal notation (e.g. "4,7 kΩ", "2,2uF")
were parsed incorrectly because the regex only recognized dots. Now
commas are normalized to dots before parsing, matching the existing
pattern used elsewhere in the codebase (PartNormalizer, price providers).
* Add unit price and extended price columns to project BOM table
Adds two optional columns to the project BOM datatable (hidden by
default, toggleable via column visibility):
- **Price**: unit price for the BOM entry in the base currency,
looked up via PricedetailHelper. For parts whose BOM quantity falls
below the minimum order amount the minimum order amount is used for
the price tier lookup so that a price is always returned.
- **Extended Price**: unit price multiplied by the BOM quantity.
Prices are rendered via MoneyFormatter (locale-aware, with currency
symbol). Both columns round up to 2 decimal places to avoid displaying
0.00 for very small prices.
* Add translation key for project.bom.ext_price
Adds the English translation "Extended Price" for the new BOM extended
price column. Other languages are marked needs-translation and will be
picked up by Crowdin.
* Add build price summary to project info tab
Displays the total BOM price for N builds on the project info page,
using the existing price-tier logic from PricedetailHelper. The user
can adjust the number of builds via a small form; the unit price is
also shown when N > 1.
New backend:
- ProjectBuildHelper gains calculateTotalBuildPrice(),
calculateUnitBuildPrice(), roundedTotalBuildPrice(), and
roundedUnitBuildPrice() — bulk-order quantities are factored in so
that price tiers apply correctly across N builds.
- ProjectController::info() now reads ?n= and passes number_of_builds
to the template.
Template (_info.html.twig):
- Adds price badge (hidden when no pricing data is available).
- Adds number-of-builds form that reloads the info page.
* Add tests for build price calculation in ProjectBuildHelper
Covers calculateTotalBuildPrice(), calculateUnitBuildPrice(),
roundedTotalBuildPrice(), and the private getBomEntryUnitPrice()
helper. Scenarios tested: empty project, no pricing data, non-part BOM
entries with manual prices, part entries with pricedetails, mixed
entries, rounding-up of sub-cent prices, and minimum order amount
floor for price tier lookup.
* Deduplicate BOM entry price logic into ProjectBuildHelper
The private getBomEntryUnitPrice() in ProjectBomEntriesDataTable was
identical to the one in ProjectBuildHelper. Replaced it with a new
public getEntryUnitPrice() on ProjectBuildHelper (returns BigDecimal,
never null) and delegate to it from the DataTable.
This eliminates the duplicate code and brings the DataTable lines under
the existing ProjectBuildHelper test coverage. Added three tests for
getEntryUnitPrice() covering the no-pricing, non-part, and part cases.
* Added type hint to service
---------
Co-authored-by: Jan Böhmer <mail@jan-boehmer.de>
* Add admin editor for KiCad autocomplete lists
* Add custom KiCad autocomplete list settings
* Ignore the footprints_custom.txt and symbols_custom.txt in git and create them on the fly if needed
Otherwise it breaks the update mechanism
* Added comments
* Include kicad custom files in config backup command
---------
Co-authored-by: Jan Böhmer <mail@jan-boehmer.de>
* Fix identation
* Allow ordering of column Storage Locations in BOM fix-#1152
* Fix "[Semantical Error] line 0, col 274 near 'storageLocations.name))': Error: 'storageLocations' is not defined." when trying to sort by column Storage Locations
* Try to fix "Iterate with fetch join in class App\Entity\Parts\PartLot using association part not allowed." when opening BOM
* Revert "Try to fix "Iterate with fetch join in class App\Entity\Parts\PartLot using association part not allowed." when opening BOM"
This reverts commit 5c5c7cece1.
* Try to fix "Iterate with fetch join in class App\Entity\Parts\PartLot using association part not allowed." when opening BOM 2nd try
* Remove alias to fix: Unknown named parameter $alias
* Reformat code to allow easier diff between ProjectBomEntriesDataTable.php and PartsDataTable.php
* Try if 'data' es really needed as it is not used in PartDataTable.php
* Use TwoStepORMAdapter to enable sorting based on other columns like storage location, manufacturing status
* Add readonly hint to projectBom query
---------
Co-authored-by: root <root@part-db.fritz.box>
Co-authored-by: Jan Böhmer <mail@jan-boehmer.de>
* Fix creating TME parts with percent signs in SPN
The SPN ends up in the URL, which later causes validation errors n the
form. Solved by encoding the percent sign.
* Add TME provider unit tests.
* Implement parsing of TME QR codes
They are present on parts purchased on tme.eu. It's based on the LCSC
parser. Some older codes I found are in upper-case so I handle those
too.
* Removed unused method
* Fixed translation message keys
* Try to find TME part via SPN
---------
Co-authored-by: Jan Böhmer <mail@jan-boehmer.de>
* Enhance barcode format checking in isFormat06Code
Updated isFormat06Code method to handle additional barcode formats for compatibility with older Mouser parts and Eyoyo barcode scanners that don't omit the record separator character
* Added tests
---------
Co-authored-by: Jan Böhmer <mail@jan-boehmer.de>
* Add manual backup creation and delete buttons to Update Manager
- Add "Create Backup" button in the backups tab for on-demand backups
- Add delete buttons (trash icons) for update logs and backups
- New controller routes with CSRF protection and permission checks
- Use data-turbo-confirm for CSP-safe confirmation dialogs
- Add deleteLog() method to UpdateExecutor with filename validation
* Add Docker backup support: download button, SQLite restore fix, decouple from auto-update
- Decouple backup creation/restore UI from can_auto_update so Docker
and other non-git installations can use backup features
- Add backup download endpoint for saving backups externally
- Fix SQLite restore to use configured DATABASE_URL path instead of
hardcoded var/app.db (affects Docker and custom SQLite paths)
- Show Docker-specific warning about var/backups/ not being persisted
- Pass is_docker flag to template via InstallationTypeDetector
* Add tests for backup/update manager improvements
- Controller tests: auth, CSRF validation, 404 for missing backups, restore disabled check
- UpdateExecutor: deleteLog validation, non-existent file, successful deletion
- BackupManager: deleteBackup validation for missing/non-zip files
* Fix test failures: add locale prefix to URLs, correct log directory path
* Fix auth test: expect 401 instead of redirect for HTTP Basic auth
* Improve test coverage for update manager controller
Add happy-path tests for backup creation, deletion, download,
and log deletion with valid CSRF tokens. Also test the locked
state blocking backup creation.
* Fix CSRF tests: initialize session before getting tokens
* Fix CSRF tests: extract tokens from rendered page HTML
* Harden backup security: password confirmation, CSRF, env toggle
Address security review feedback from jbtronics:
- Add IS_AUTHENTICATED_FULLY to all sensitive endpoints (create/delete
backup, delete log, download backup, start update, restore)
- Change backup download from GET to POST with CSRF token
- Require password confirmation before downloading backups (backups
contain sensitive data like password hashes and secrets)
- Add DISABLE_BACKUP_DOWNLOAD env var (default: disabled) to control
whether backup downloads are allowed
- Add password confirmation modal with security warning in template
- Add comprehensive tests: auth checks, env var blocking, POST-only
enforcement, status/progress endpoint auth
* Fix download modal: use per-backup modals for CSP/Turbo compatibility
- Replace shared modal + inline JS with per-backup modals that have
filename pre-set in hidden fields (no JavaScript needed)
- Add data-turbo="false" to download forms for native browser handling
- Add data-bs-dismiss="modal" to submit button to auto-close modal
- Add hidden username field for Chrome accessibility best practice
- Fix test: GET on POST-only route returns 404 not 405
* Fixed translation keys
* Fixed text justification in download modal
* Hardenened security of deleteLogEndpoint
* Show whether backup, restores and updates are allowed or disabled by sysadmin on update manager
* Added documentation for update manager related env variables
---------
Co-authored-by: Jan Böhmer <mail@jan-boehmer.de>
After cache warmup, create a temporary PHP script in the public
directory and invoke it via HTTP to reset OPcache in the PHP-FPM
context. This prevents stale bytecode from causing 500 errors when
the progress page refreshes after code has been updated.
The reset is also performed after rollback and during restore.
Uses a random token in the filename for security, and the script
self-deletes after execution with a cleanup in the finally block.