rossaddison/invoice

Builds the invoice folder for rossaddison/yii3-i

Maintainers

Package info

github.com/rossaddison/invoice

Forum

Type:project

pkg:composer/rossaddison/invoice

Transparency log

Fund package maintenance!

Open Collective

yiisoft

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 2

dev-main 2026-07-09 19:48 UTC

README

Yii3 License stable
Downloads
Build Dependency Checker Static Analysis Psalm Level type-coverage Coverage Code Smells PHP-CS-Fixer Stats Hosted by Vultr

(Place the contents of this download into the yii3-i invoice folder or run as a separate repository.)

Yii3-i (Rossaddison/Invoice)

A professional Open Source E-Invoicing System for PHP (Yii3) with UBL 2.4 and Peppol support.

Features

Vat Support

Multi-Currency Billing

Peppol UBL 2.4 E-Invoicing

Automated generation and transmission of compliant UBL 2.4 documents via the Peppol network.

Recent Implementations

HMRC MTD Developer Sandbox — OAuth2 Backend IntegrationHmrcApiCatalogue curates 8 MTD APIs (VAT, Self Assessment, Self-employed Business, etc.) with scope, identifier type (VRN/NINO/EORI), and route map; backend/hmrc dashboard shows a Full API Catalogue card (always visible, rows green-highlighted when within granted token scope), an Available APIs dropdown driving navigation to API-specific controller actions, and a "Log in with HMRC" button that initiates PKCE OAuth via auth/authclient; callbackDeveloperGovSandboxHmrc now checks getIdentity()->getId() !== null immediately after storing the 5 HMRC session tokens — if the admin is already authenticated it redirects straight to backend/hmrc without switching the session user; the login-page "Continue with Developer Gov Sandbox UK" button and its no_developer_sandbox_hmrc_continue_button setting were removed entirely — HMRC OAuth is now exclusively for API authorisation from backend/hmrc. Psalm errorLevel 1 clean (July 2026)

Mockery Bridge — Testo Integrationtesto/bridge-mockery wired via MockeryPlugin in testo.php; Mockery::close() called automatically after every test so expectations are always verified with no per-test tearDown() boilerplate. Solves three pain points in this codebase: final Cycle ORM repository classes that PHPUnit's createMock() cannot subclass; fluent expects()/allows()/spy() API that separates strict expectations from stubs; and automatic teardown that prevents silently-skipped mock assertions. First use: As4RetryEngineTest — three detectMissingReceipts() scenarios (empty queue, null firstSentAt, EBMS:0301 timeout) all tested against interface mocks with no database. Psalm errorLevel 1 clean (July 2026)

Auth Controllers — Full Hardening Implementation — Seven-fix bot-hardening pattern extended to all five auth routes: /change (10/60 s global, 3/60 s per-IP), /forgotpassword (5/60 s global, 2/60 s per-IP — strictest, triggers email), /resetpassword/{token} (10/60 s global, 3/60 s per-IP — already token-gated). All routes now have LimitAlways + LimitCallback CF-Connecting-IP + TooManyRequestsMiddleware on both layers + Turnstile widget + pre-hydration verify + checkRateLimit() with distinct key prefixes. TurnstileVerification trait now shared by all four auth controllers. Repository interfaces introduced (InvRepositoryInterface, InvItemRepositoryInterface, SettingRepositoryInterface, etc.) to unblock mocking of final classes in PHPUnit. Dual @dataProvider + #[DataProvider] annotation pattern established for PHPUnit 13 + Codeception compatibility. PHPUnit 3,727 OK · Codeception 3,673 OK · Psalm no errors (July 2026)

Change Password Route — Bot Susceptibility Analysis — Assessment of /change against the seven fixes applied to /login (July 2026): route has no middleware (fixes #1–#3 all missing); FormHydrator runs before any IP check (fix #4 missing); AuthSecurityHelper not injected so checkRateLimit() is never called (fixes #6–#7 missing). Key mitigation: isGuest() guard means unauthenticated bots cannot reach POST processing, narrowing the threat to compromised authenticated sessions and scripted abuse. Priority gaps: no route-level rate limiting (High), checkRateLimit() absent (High), hydration before IP check (Medium), no Turnstile (Low–Medium). Recommended fix mirrors the login pattern: LimitAlways + LimitCallback closures in routes.php, AuthSecurityHelper injected, checkRateLimit() called before hydration, storage key prefix sha1('change_ctrl' . $ip) to avoid GCRA collision (July 2026)

Login Route — Hardening Implementation — Seven fixes applied to /login after the bot-susceptibility analysis (July 2026): LimitCallback reading CF-Connecting-IP for correct per-IP GCRA buckets; LimitAlways global counter (30/60 s) collapsing distributed botnet traffic; TooManyRequestsMiddleware on both rate-limit layers so FileCache CAS contention returns 429; Turnstile widget on the login form with token verification before FormHydrator runs; inner per-IP Counter(5,60) decoupled from the old test-inflated limit; HTTP_CF_CONNECTING_IP first in AuthSecurityHelper::getClientIpAddress() header chain; checkRateLimit() wired into login() with a distinct key prefix to activate the defence-in-depth layer. verifyTurnstile() extracted to a shared TurnstileVerification trait used by both AuthController and SignupController, eliminating the duplication SonarQube would have flagged. Psalm errorLevel 1 clean (July 2026)

Login Route — Bot Susceptibility Analysis — Seven deficiencies in the current /login protection (July 2026): LimitPerIp reads REMOTE_ADDR which is Cloudflare's edge IP behind the proxy — all users and bots share one bucket; no LimitAlways global path counter means a 900-IP botnet can deliver 18,000 attempts per 10 s before any per-IP bucket triggers; no failStoreUpdatedDataMiddleware means FileCache CAS contention silently forwards requests; no Turnstile on the login form allows unlimited automated credential-stuffing at zero CAPTCHA cost; the DI-bound limit of 20/10 s was raised for test-suite compatibility, not security, allowing 120 guesses per minute per IP; HTTP_CF_CONNECTING_IP is absent from AuthSecurityHelper::getClientIpAddress() header chain; checkRateLimit() is never called from login() leaving a defence-in-depth layer disconnected. Partial mitigations: 2FA (when enabled), userInv->getActive() gate, session ID regeneration on success — all operate after authentication, not before (July 2026)

Rate Limiter & Signup — Bot-Wave Hardening — Four fixes implemented after a 900-bot signup wave (July 2026): LimitCallback reading CF-Connecting-IP so the GCRA bucket fingerprints the real client IP rather than Cloudflare's edge IP (REMOTE_ADDR); a layered LimitAlways global path counter (50/10 s) that rejects the botnet's combined traffic before any per-IP bucket is consulted; TooManyRequestsMiddleware wired as the failStoreUpdatedDataMiddleware on both rate-limit layers so FileCache CAS contention returns 429 instead of silently forwarding the request; and verifyTurnstile() moved before FormHydrator::populateFromPostAndValidate() so malformed POST bodies that would fail validation early can no longer bypass Turnstile verification. Deployment assumption: direct origin access must be blocked at the firewall so CF-Connecting-IP cannot be spoofed. Psalm errorLevel 1 clean (July 2026)

Rate Limiter & Signup Bot-Protection — Known Limitations and Fixes — Six structural limitations in LimitRequestsMiddleware exposed by a 900-bot signup wave (July 2026): REMOTE_ADDR wrong behind Cloudflare's proxy (critical — real IP is in CF-Connecting-IP); per-IP GCRA bucket useless against botnets with distinct IPs; FileCache CAS failure silently allows requests through when no failStoreUpdatedDataMiddleware is set; Turnstile verification fires only after form validation passes, letting malformed POSTs bypass it for free; rate-limit headers (X-Rate-Limit-Reset) advertise the retry window to bots; GET and POST counted in separate buckets doubling the effective attempt allowance. Fix summary: LimitCallback reading CF-Connecting-IP, layered LimitAlways global counter, APCu storage or explicit fail-middleware, move Turnstile before hydration.

Golf Lessons & the Peppol Network — A Treasurer's Guide — A non-technical explainer for golf club treasurers who have never heard of Peppol: the full booking lifecycle from Purchase Order to Receipt Advice told in golf terms (England Golf register = SML, club mailroom = Access Point, official scorecard format = UBL 2.4, recorded-delivery envelope = AS4); the four-corner model illustrated with the PGA Pro ↔ Golf Club flow; what Yii3-i does at each corner (outbound: SoapEnvelopeBuilderWsSecuritySignerAs4HttpClient; inbound: As4ReceiveControllerAs4DuplicateDetectorAs4InvoiceImportService); the bilateral demo setup (localhost = club, yii3i.online = pro, ngrok for reverse direction); a full Peppol ↔ Golf glossary; and the one-component swap (StaticAs4SmpResolverAs4SmpResolver) that turns the demo into production Peppol (July 2026)

AS4 Bilateral Test Infrastructure — StaticAs4SmpResolver + as4/test-sendStaticAs4SmpResolver implements As4SmpResolverInterface and returns a fixed As4SmpEndpoint from env config, bypassing SMP/SML DNS for direct node-to-node testing between localhost and yii3i.online; php yii as4/test-send command sends a minimal bilateral ping XML (or a supplied UBL file) through the full outbound pipeline — WsSecuritySigner → SoapEnvelopeBuilder → As4HttpClient → peer /as4/receive — and reports HTTP status, receipt signal, and error detail; config/common/di/as4.php wired with 9 outbound bindings including PEM strings loaded from env-specified cert file paths; AS4_SIGNING_KEY_PATH, AS4_SIGNING_CERT_PATH, AS4_PEER_CERT_PATH, AS4_PEER_ENDPOINT, AS4_PEER_PARTY_ID, AS4_SENDER_PARTY_ID, AS4_PEER_TRANSPORT_PROFILE, AS4_RETRY_POLICY added to .env.example; swap StaticAs4SmpResolverAs4SmpResolver when moving to real Peppol 4-corner SMP lookup; Psalm errorLevel 1 clean (July 2026)

OpenPeppol Service Provider Certification — Preparation Guide — Six-section guide covering OpenPeppol membership application (APCA or SCA tier), test-pilot environment setup (test SMP + EFTIA interoperability lab), pre-certification interoperability checklist with cross-references to code (As4ReceiveController, As4RetryEngine, WsSecuritySigner, CycleOrmAs4MessageRepository), production certification steps (EFTIA test suite → conformance certificate → SML registration), post-certification obligations (annual recertification, cert renewal, Peppol Directory maintenance, changelog monitoring), and recommended production cron schedule for as4/retry, as4/monitor, and as4/status (July 2026)

Peppol PKI Certificate Request (CSR) — Template & Guide — OpenPeppol AP certificate CSR guide: exact subject field format (C=GB/O=.../OU=PEPPOL/CN=PNO<country>:<participantId>), three-step OpenSSL commands for key + CSR generation, Member Portal submission steps, install + verify commands (modulus fingerprint comparison, subject and validity checks), php yii as4/monitor --warn-days=30 cron for expiry alerting, file permission hardening (chmod 400 private key, chmod 700 cert directory), annual renewal checklist including requirement to generate a new key (not reuse the old one) and update Peppol Directory if the certificate fingerprint changes (July 2026)

AS4 Phase 2 — Message Log Dashboard & Console ToolingAs4MessageController with /as4/messages index (state-badged table: pending/sent/receiptReceived/delivered/failed/duplicate/received with Bootstrap colour coding) and /as4/messages/view/{id} detail page (6 cards: Identity, Routing, Retry State, Receipt, Error, Timestamps); PeppolMessageController with /peppol/messages index (inv link, status badge, retry count, sent/delivered/created) and detail page (Identity, Delivery, Error, UBL XML collapsible with copy button); four authenticated routes added before the public as4/receive endpoint; as4/retry command calls As4RetryEngine::processRetries() and detectMissingReceipts() — cron */5 * * * *; as4/status prints a per-state count table from findAllMessages(), warns if FAILED > 0; as4/resend --message-id=<id> confirms interactively, parses stored SOAP XML, calls sender, maps result to markReceiptReceived() or markSent(); as4/monitor --signing-cert --warn-days=30 checks cert expiry via openssl_x509_parse(), counts FAILED messages, exits code 1 if any issue (cron-alert friendly); all four commands registered in config/console/params.php; Psalm errorLevel 1 clean (July 2026)

AS4 Complete Implementation — Summary — Production-ready eDelivery AS4 2.0 implementation: class inventory, SOAP 1.2 envelope construction, ebMS3 UserMessage/SignalMessage, WS-Security Ed25519 signing + X25519 HKDF-AES-128-GCM encryption, outbound retry engine, inbound duplicate detection, receipt generation, and bilateral localhost ↔ yii3i.online test harness (July 2026)

AS4 Complete Implementation Guide — Directory-level reference for the AS4 stack: production checklist, environment variable reference (AS4_SIGNING_KEY_PATH, AS4_PEER_ENDPOINT, AS4_RETRY_POLICY, etc.), DI wiring in config/common/di/as4.php, quick-start commands, and swap path from bilateral test mode to real Peppol 4-corner SMP lookup (July 2026)

AS4 Integration Checklist — Phase-by-phase verification checklist — Project Setup → Core Classes → Outbound Pipeline → Inbound Pipeline → Console Commands → Production — with php yii verification commands at each milestone (July 2026)

AS4 Implementation Guide for eDelivery 2.0 — Complete XML and PHP reference for the eDelivery AS4 2.0 Common Profile: annotated SOAP 1.2 envelope, ebMS3 header structure, WS-Security BinarySecurityToken placement, Ed25519 signature computation, X25519 key agreement for payload encryption, and PHP class mapping to each XML layer (July 2026)

RBAC Bridge Table — user_rbac_linkUserRbacLink Cycle ORM entity bridges userinv.user_id (INT) to yii_rbac_assignment.user_id (VARCHAR); auto-increment id PK + UNIQUE indexes on user_id and rbac_user_id; FK ON DELETE RESTRICT prevents orphaned RBAC rows; syncIfEmpty() backfills from AssignmentsStorageInterface::getAll() on first /invoice load; new signups get a bridge row immediately via assignSignupRole() before admin activation; AppConstants::ROLE_ADMIN/OBSERVER/ACCOUNTANT constants eliminate S1192 duplication across 7 files; REDIRECT_USERINV_INDEX constant eliminates 14 occurrences in UserInvController; Psalm errorLevel 1 clean (July 2026)

Credit Note Workflow — read_only_toggle InteractionCreate Credit Invoice toolbar button appears for any Sent / Viewed / Paid invoice (reqStatusId() >= 2) or when is_read_only = true, provided the user has editInv permission and no credit note already exists (creditinvoice_parent_id = null); read_only_toggle setting (Sent = Peppol Requirement, Paid = Relaxed/General Use) is prospective only — changing it does not retroactively update existing invoice records; widened credit-button condition from === 4 to >= 2 so Sent/Viewed invoices with is_read_only = false no longer require a manual status-to-Paid workaround; createCreditConfirm() fixed to read group_id from request instead of hardcoded 4; GroupRepository::generateNumber() now throws \RuntimeException with descriptive message when group is missing; SQL alignment queries documented for DB migration after setting change (July 2026)

Batch Email — Send Selected Invoices to Clients — ☑️📧 Email Client toolbar button on inv/index; select invoices via checkboxes, confirmation modal shows per-invoice email preview, choose a From email from FromDropDown (➕ button adds a new verified sender and redirects back to the modal via ?openModal=batchEmail), choose an email template, confirm → one email per selected invoice with its PDF attached, each invoice marked Sent (status 2), InvSentLog entries created; {{{invoice_table}}} placeholder renders a responsive HTML table; FromDropDownRepository wired through InvIndexNavDeps → InvsListWidget → InvsToolbarParams → InvsToolbar; from_dropdown_id overrides the UserInv / EmailTemplate from-address fallback; MailerHelper::yiiMailerSend signature changed from ?string to array $pdfPaths; InvBatchEmailDeps value object avoids SonarQube S107; Psalm errorLevel 1 clean (June 2026)

Quick Pay — Per-Row Inline & Bulk Toolbar Payment — 💰 button added immediately right of the Status column on inv/index; clicking expands an HTMX inline form (date + bank ref) that calls inv/quickpay, saves a Payment via PaymentService, and recalculates inv_amount + status via InvRecalculator; fully-paid invoices show a ✅ YYYY-MM-DD badge instead; ☑️💰 Quick Pay toolbar button opens a Bootstrap 5 modal for all checked invoices (inv/bulkquickpay); date input uses showPicker() for native calendar on click; Payment BelongsTo annotation corrected from nullable: falsenullable: true (DB column was already nullable) fixing Cycle\ORM\Exception\Relation\NullException when invoice has no payment method set; three new GET routes; TypeScript handleBulkQuickPay(); quick.pay / bank.ref translation keys; Psalm errorLevel 1 clean (June 2026)

Invoice Copy — Spreadsheet Import#modal-copy-inv-multiple on inv/index gains a CSV import section: download a four-column template (date_created, note, same_amount, payment_date), upload a filled CSV, preview parsed rows, then click Import Spreadsheet to bulk-copy selected invoices with per-row date, note, amount flag, and optional payment; CsvDateNormaliser round-trip validates six input formats (Y-m-d, d/m/y, d/m/Y, m/d/Y, d-m-Y, d.m.Y) to prevent PHP createFromFormat month-overflow silently producing wrong dates; PaymentService::savePayment guarded against false from createFromFormat; endpoint uses GET with rows_json param matching the multiplecopy pattern (bypasses CSRF and Apache redirect issues); parseCopyCsv() auto-detects , or ; delimiter; 10 Testo tests in CsvDateNormaliserTest; Psalm errorLevel 1 clean; TypeScript bundle rebuilt 146.4 kb (June 2026)

.env Overwritten on git pull — Untrack from Git Index.env was committed to the repo despite its .gitignore entry; once tracked, .gitignore is ignored by git, so every pull overwrites production credentials; fix: git rm --cached .env removes it from the index without touching the file on disk; .gitignore then takes effect permanently (June 2026)

Invoice Index Filters Broken — RequestInputParametersResolver Missing — All inv/index filter dropdowns (invoice number, client, status, year-month, family name, etc.) silently returned the full unfiltered row set on every selection; root cause: InvIndexFilter implements RequestInputInterface with class-level #[FromQuery] but RequestInputParametersResolver was never added to CompositeParametersResolver in config/web/di/middleware-dispatcher.php — Yii3 DI therefore injected a blank DTO with all properties null, isset(null) is always false, so no filter branch ever fired; fix: one line adding Reference::to(RequestInputParametersResolver::class) to the composite (vendor DI definition already present); secondary fix: $nameParts[1] ?? '' guard in InvFilterTrait for single-word client names; Psalm errorLevel 1 clean (June 2026)

Testo Integration — PHP Testing Framework — Testo (php-testo/testo) runs alongside PHPUnit; #[Test] attribute style matches Cycle ORM entity mapping; testo/facade + yiisoft/injector injects real Yii3 services into test methods, eliminating mock boilerplate for integration tests; #[ExpectException] accepts class only — message assertions use try/catch; no mock library yet (roadmap issue #41 — Mockery bridge); migration strategy: DDD entity + helper tests migrate now, service/controller tests stay in PHPUnit until #41; dual runner in composer.json; SonarCloud merges both Clover coverage files; two working examples in Tests/Testo/ (FamilyTest, CacheDiTest) (June 2026)

Invoice Copy — Multi-Client Selection & Workflow Badge Fix#modal-copy-inv-multiple on inv/index extended with a live-filter client checkbox list so selected invoices can be copied to any combination of clients in one step; ProductClient pivot synced after each copy; multiplecopy() falls back to the invoice's own client when no selection is made; workflow-type badge bug fixed — getSoId() !== null> 0 so plain invoices copied with so_id = 0 no longer show the 🔀 Peppol badge; modal rewritten to Yiisoft\Html\Html as H conventions; TypeScript bundle rebuilt (144.1 kb); Psalm errorLevel 1 clean (June 2026)

PBES2 p2c Unbounded Iteration Count — CPU-Amplification DoSweb-token/jwt-framework transitive dep (via rossaddison/yii-auth-client); GHSA-3prj-6hqw-cm82; affected <= 4.1.6; installed 4.1.7 already contains the fix — DEFAULT_MAX_COUNT = 1_000_000 constant and p2c > max_count guard in PBES2AESKW::checkHeaderAdditionalParameters() enforced before any hash_pbkdf2() call; project also never registers PBES2 algorithms; no PR required; logged in snyk-resolved.db as resolved (June 2026)

JWT Framework — JWE Algorithm Confusion Fixweb-token/jwt-framework transitive dependency (via rossaddison/yii-auth-client); JWSVerifier already safe; JWEDecrypter passed merged protected+unprotected header to getKeyEncryptionAlgorithm() and getContentEncryptionAlgorithm() — last-wins array_merge() allowed attacker to override alg/enc via unprotected header (TOCTOU split, RFC 7516 §4.1.1/§4.1.2); fix reads both parameters exclusively from getSharedProtectedHeader(); $completeHeader preserved for decryptCEK() (ECDH epk/apu/apv legitimately in unprotected headers); is_string() guards added; contributed upstream as web-token/jwt-framework #658 (June 2026)

Dev Tools Console Improvements — Windows proc_open colour fix (CRT vs Win32 env blocks; FORCE_COLOR passed explicitly as 5th arg); ANSI background codes 40–107 + underline added so Psalm's green/red summary blocks render correctly; OSC 8 terminal hyperlinks converted to clickable HTML anchors (composer outdated package names open GitHub); Copy button on output panel (plain-text clipboard, green "Copied!" flash); SonarCloud Filter by Rule Key cascading dropdowns — language selector triggers live ?api=failing_rules fetch, rule-number dropdown populated with only the S#### codes currently failing for that language (June 2026)

Dev Tools Web UI — m.bat / m.phpm.bat replaced by a PHP built-in web server (php -S 127.0.0.1:8099 m.php) eliminating all batch-file stdin issues; 16 category submenus (Psalm, Composer, Node, TypeScript, Angular, Testing, Snyk, PHP-CS-Fixer, PHPCS, Rector, SonarCloud, Yii, GitHub, Peppol, Benchmarks, System); streaming output via proc_open() + ReadableStream; ANSI colour rendering; Bootstrap 5.3 dark theme; session-stored SonarCloud/GitHub tokens; Snyk Resolved Vulnerabilities Index (SQLite, seeded from .snyk, committed to repo, CWE advisory links); SQLite setup guide distinguishing CLI PHP from WAMP Apache PHP; 16 local SVG menu icons (Simple Icons for brands, Bootstrap Icons for generics, official Yii3 logo in brand colours) served statically via built-in server passthrough; Bootstrap 5 hover/focus popovers on each category card listing every submenu command for discoverability (June 2026)

AS4 Access Point — Bilateral & Peppol Roadmap — Living roadmap for the native AS4 Access Point built in PHP; outbound stack complete (As4RetryEngine, CycleOrmAs4MessageRepository, As4RetryPolicyInterface, As4SenderInterface, ebMS3 signal detection, atomic concurrency claim, 15 PHPUnit tests); Phase 1 plans the inbound pipeline (As4Receiver, As4SignatureVerifier, As4DuplicateDetector, As4ReceiptGenerator, As4ReceiveController) for bilateral testing between localhost and yii3i.online without Peppol PKI; Phase 2 maps the small delta to a full Peppol 4-corner Access Point (SMP lookup already built, Peppol-issued certificate + SML registration + EFTIA conformance remaining) (June 2026)

Oxalis Access Point — Localhost Setup — Phase A: docker-compose up oxalis-mock runs a WireMock stub on port 8181 (no certificate needed, works today); Phase B: real Oxalis AS4 container on port 8080 once a test certificate is obtained from a Peppol AP provider; inbound callback wired to POST /peppol/inbound/delivery; all four env vars documented in .env.example (June 2026)

Invoice Index — Workflow Type Badges — Always-visible emoji badge column in InvsListWidget distinguishes standalone invoices (📄 grey), quote-derived invoices (💬→📄 teal), and full Peppol observer-workflow invoices (🔀 blue, so_id set); tooltips show full chain in UI language; peppol_workflow added to Group By dropdown with three named groups; Psalm errorLevel 1 clean (June 2026)

Peppol Code-List Currency Checkbin/check-peppol-codelists.php queries the GitHub Commits API for each of the five VEFA XML files in DownloadedXml/ and compares the last upstream commit date against the recorded download date; green = UP-TO-DATE, red = STALE; optional GITHUB_TOKEN raises rate limit from 60 to 5 000/hr; exit code 1 when stale (CI-friendly); exposed via m.bat [27], make peppol-check, and composer run peppol:check (June 2026)

Peppol XML Code-List Loaders — PeppolArrays refactor — Six S138 violations eliminated by replacing ~2 900 lines of hardcoded PHP arrays with a shared private static loadVefaCodeList(string $filename) that reads OpenPEPPOL VEFA-format XML at runtime via DOMXPath; Yiisoft\Aliases resolves @peppol to __DIR__; five XML files in DownloadedXml/ cover UNCL7143, ISO 6523 ICD, UNCL7161, UNCL5305, and EAS; six dead data files removed via git rm; psalm.xml UnusedVariable block updated; electronicAddressScheme() view key references migrated from code/description to Id/Name; upstream URL + quarterly-update note in resources/peppol/uncl2005.php establishes currency trail for UNCL2005 subset (June 2026)

SonarQube S107 — QuoteController + SalesOrderControllerQuoteController 32p → 6p via QuoteControllerBaseDeps (7p), QuoteControllerInvDeps (6p), QuoteControllerQuoteDeps (6p), QuoteControllerSoDeps (5p), QuoteControllerInfraDeps (6p), QuoteControllerUIDeps (2p); SalesOrderController 17p → 3p via SoControllerBaseDeps (7p), SoControllerInvDeps (5p), SoControllerMiscDeps (3p); 2 dead params dropped from SalesOrderController (InvAmountService, IIACS); all properties re-declared at class level — zero trait/method files changed; Psalm errorLevel 1 clean (June 2026)

SonarQube S107 — InvController::__construct — 23-param constructor reduced to 4 params via InvControllerBaseDeps (7p: webService/userService/translator/webViewRenderer/session/sR/flash), InvControllerServiceDeps (6p: invAllowanceChargeService/invAmountService/invService/invCustomService/invItemService/invTaxRateService), InvControllerInfraDeps (6p: factory/htmlResponseFactory/logger/mailer/urlGenerator/delRepo), InvControllerUIDeps (4p: aciis/formFields/buttonsToolbarFull/customFieldProcessor); all 16 properties re-declared at class level and assigned from deps — zero trait files changed; Psalm errorLevel 1 clean (June 2026)

SonarQube S107 — QuoteEmailStage2Deps + QuoteEmailStage0Deps sub-split — 14-param QuoteEmailStage2Deps reduced to 3 params; 10-param QuoteEmailStage0Deps reduced to 2 params; shared QuoteEmailCustomDeps (6p: ccR/cfR/cvR/icR/pcR/qcR) used by both stages; QuoteEmailStage0EntityDeps (4p: etR/qR/socR/uiR); QuoteEmailStage2CoreDeps (5p: gR/iaR/iR/socR/uiR); QuoteEmailStage2RelationDeps (3p: qaR/qR/soR); Quote/Trait/Email.php updated throughout; Psalm errorLevel 1 clean (June 2026)

SonarQube S107 — InvEmailStage2Deps sub-split — 15-param InvEmailStage2Deps reduced to 3 params by introducing InvEmailCoreDeps (6p: iR/iaR/icR/islR/gR/uiR), InvEmailCustomDeps (6p: ccR/cfR/cvR/pcR/qcR/socR), InvEmailRelationDeps (3p: qaR/qR/soR); InvEmailService and Email.php trait updated throughout; Psalm errorLevel 1 clean (June 2026)

SonarQube S107 — SalesOrderViewService — 23-param SalesOrderViewDependencies replaced by four sub-groups + SalesOrderViewService (4p): SoViewCoreDeps (6p: soR/soaR/soiR/sotrR/socR/soiaR), SoViewItemDeps (5p: piR/pR/taskR/trR/uR), SoViewMetaDeps (5p: cfR/cvR/gR/invRepo/settingRepository), SoViewRelationDeps (6p: acsoiR/acsoR/dR/qR/ucR/uiR); dead cR param dropped; SalesOrderViewDependencies deleted; Psalm errorLevel 1 clean (June 2026)

SonarQube S107 — InvEmailService and InvViewService — Two S107 Application Services added: InvEmailService (7p) consolidates email send logic from the Email trait; InvEmailStage2Deps trimmed from 22 to 15 params by dropping 7 dead repos never referenced in Email.php; InvEmailStage1Data::$from: array replaced with typed $fromEmail/$fromName string fields; PSR-7 body extraction fixed using /** @var array $body['MailerInvForm'] */ (matches established Quote email trait pattern — avoids MixedAssignment/MixedArrayAccess without @psalm-suppress); InvViewService (5p) replaces the 29-param InvViewDeps by splitting into five sub-groups — InvViewCoreDeps (6p: iR/iaR/icR/irR/pymR/gR), InvViewItemDeps (6p), InvViewMetaDeps (6p), InvViewAllowanceDeps (5p), InvViewRelationDeps (6p); InvViewDeps deleted; View.php trait updated throughout; Psalm errorLevel 1 clean (June 2026)

SonarQube S1144 — False Positive: Private Methods Called Across Trait Boundaries — SonarQube cannot trace $this->method() calls that cross PHP trait file boundaries, so it incorrectly reports private class methods as unused when their only callers live in a composed trait; concrete example: displayEditDeleteButtons and flashNoEnabledGateways in InvController.php called from View.php, and redirectToAdminMustMakeActive in AuthController.php called from Callback.php; fix: change private to protected — S1144 only fires on private methods (June 2026)

SonarQube — S1142, S1448, S131, S3776 fixes — Five violations resolved: InvPdfService::generateHtml reduced from 4 returns to 2 by combining null guards; PaymentInformationController switch gained default: break; (S131); brainTreeInForm reduced from 4 returns to 3 by extracting initializeBraintree(): ?array; ProductController::add() cognitive complexity dropped from 36 to 2 by extracting handleAddPost and saveProductCustomFields; SalesOrdersListWidget dropped from 30 methods to 18 by extracting SalesOrdersColumnBuilder (8 column builders) and SalesOrdersGroupingRenderer (4 grouping helpers) into the same Widget\ namespace — Psalm errorLevel 1 clean throughout (June 2026)

SonarQube S107 — customValues() contract fix — Runtime fix applied to InvPdfService, QuotePdfService, and SalesOrderPdfService: customValues() must store entity objects ($values[] = $entity), not extracted strings — CustomValuesHelper::formValue() calls ->reqCustomFieldId() on each array element and never reads the array keys; array keys are irrelevant (June 2026)

SonarQube S107 — SalesOrderPdfService and PdfHelper.php deletedPdfHelper::generateSalesorderPdf (17p) moved into SalesOrderPdfService; SalesOrderController::pdf reduced from inline PdfHelper construction + SalesOrderViewDependencies to 2 params; SalesOrderPdfCoreDeps (4p), SalesOrderPdfDocDeps (3p — no delivery location), SalesOrderPdfItemDeps (4p); PdfHelper.php deleted entirely — zero callers remained after all three PDF services extracted; dead $pdfhelper property removed from MailerHelper; bug fix: sales order PDF now uses client language correctly (old getPrintLanguage() always returned 'English' for sales orders) (June 2026)

SonarQube S107 — QuotePdfService and Quote Email Deps — Seven S107 violations eliminated (PdfHelper::generateQuotePdf 16p, PdfTrait::pdf 15p, two pdfDashboard* at 16p each, emailStage0 12p, emailStage1 28p, emailStage2 21p); QuotePdfCoreDeps, QuotePdfDocDeps, QuotePdfItemDeps all ≤6 params; QuotePdfService exposes clean generate(), findQuote(), and uiR() methods and is resolved automatically by Yii3 DI; QuoteEmailStage0Deps, QuoteEmailStage1Data, QuoteEmailStage2Deps consolidate email-stage wiring; PdfHelper::generateQuotePdf deleted; QuoteController no longer constructs PdfHelper; QuoteEmailStage1Data::$from: array replaced with typed $fromEmail/$fromName string fields (Psalm-enforced — no @psalm-suppress) (June 2026)

SonarQube S107 — DDD Application Service Pattern (InvPdfService) — Three S107 violations eliminated (InvPdfDeps 17p, generateInvPdf 19p, generateInvHtml 18p) by replacing them with a proper Application Service; InvPdfCoreDeps, InvPdfDocDeps, and InvPdfItemDeps group related repos into ≤6-param sub-deps classes; InvPdfService exposes a clean 3-param generate() method and is resolved automatically by Yii3 DI; PdfHelper reduced by ~242 lines; PdfTrait and Email trait now inject the service directly; documents the reusable pattern for the remaining 135 S107 violations (QuotePdfService, SalesOrderPdfService, InvEmailService, etc.) (June 2026)

MTD VAT — Purchase Entries & Bridging Software StrategyPurchaseEntry lightweight entity for supplier invoice recording; CSV bridging import; VAT100 Box 4 and Box 7 auto-populated from PurchaseEntryRepository::repoVatTotalsForPeriod(); why inv_type on Inv was rejected; HMRC Developer Hub sandbox route map; PurchaseEntryVatAggregator extracted from repository so summation logic (Box 4 input VAT + Box 7 purchases ex-VAT, rounded to 2dp) is unit-testable without ORM infrastructure; 11 PHPUnit tests cover empty period, rounding, zero-rated supplies, large amounts, mixed VAT rates, and generator iterables (June 2026)

PHPUnit — Bypass Finals & 100 % PurchaseEntry Coveragedg/bypass-finals added as a dev dependency so createMock() can double final repository classes; Tests/bootstrap.php calls DG\BypassFinals::enable() before autoload; phpunit.xml.dist bootstrap updated; 37-test PurchaseEntryServiceTest covering saveEntry field mapping, date parsing, created_at guard, deleteEntry delegation, and all four VAT quarter labels for UK / calendar-year / Australian tax years; all five PurchaseEntry classes now at 100 % line coverage (June 2026)

Purchase Entry — VAT Quarter Grouping, Locale Defaults & Index UI — GridView + HTMX partial swap on purchaseentry/index; group-by toggle (All / By Month / By Supplier / By Quarter); VAT quarter key derived from this_tax_year_from_date_* settings using modular arithmetic across the year boundary; disabled "By Quarter" button with flash warning when tax year not configured; breadcrumbs linking directly to setting/tabIndex?active=taxes#settings[field] with ⏳ tooltip when unset; locale-defaults page covering ~50 countries with one-click Apply (POST saves month + day, preserves existing year); all @psalm-suppress removed; S1131/S1192/S3358 SonarQube violations resolved (June 2026)

AllowanceCharge Amount Validation and View Toggle — Cross-field validation on AllowanceChargeForm via inline Callback closures in getRules(); enforces MFN × base ÷ 100 = amount in percentage mode and rejects non-positive fixed amounts; dynamic formula in error message; two translation keys added; AllowanceChargeToggleHandler TypeScript class reads data-ac-templates from the select element and switches quoteitemallowancecharge and quoteallowancecharge forms between fixed-amount and variable (base + live formula) mode without page reload (June 2026)

Peppol Schematron Validator — Route 1SchematronRuleRunner evaluates PEPPOL-EN16931-UBL.sch directly against the invoice DOM at runtime; XPath 2.0 subset implemented in PHP (normalize-space, substring, translate, castable as, sequence constructors, for…return, axis :: steps); ten u: checksum functions wired from existing PeppolValidator methods; hand-written rule methods gated off when .sch file present (June 2026)

Peppol Schematron Code Generation — PHP/TypeScript/Scala validator files generated from the official Peppol BIS Billing 3.0 Schematron .sch file; bin/generate-php-validators.php, bin/generate-ts-validators.php, bin/generate-scala-validators.php; VO layer; PHP upgrade path replacing PeppolValidator XPath methods with a hydrator + generated functions (June 2026)

TypeScript Vitest Coverage — Vitest + jsdom + v8 coverage wired into CI for inv-index.ts, list-utils.ts, and quote-index.ts; phpunit.xml.dist case fix for Linux CI; PHP and TS coverage fed to SonarCloud; coverage badge added (May 2026)

Pre-commit TypeScript IIFE Build Hook.githooks/pre-commit rebuilds both IIFE bundles (≈ 20 ms via esbuild) and auto-stages the output before every commit so the compiled bundle is never stale relative to TypeScript source; prepare script in package.json runs git config core.hooksPath .githooks automatically after npm install on a fresh clone; esbuild invoked via node node_modules/esbuild/bin/esbuild to bypass missing .bin/ shim on Windows (June 2026)

SonarCloud First Gate — SonarCloud runs as a standalone job before the PHP matrix build; needs: [sonar] blocks all four runners until the quality gate passes; AI-assisted contributions must self-audit before commit (May 2026)

BACS Quick Pay — One-off bank-transfer modal on the invoice guest page: bank details card, per-invoice QR codes, copy-to-clipboard buttons, BacsPaymentService, 38 new PHPUnit tests; fixed gateway CDN script ordering, CSP https:// violations, and missing $bacsUnpaidInvs parameter bug (May 2026)

PCI Gateway Asset Loading — Stripe/Braintree/Amazon Pay CDN scripts moved to <head> (jsPosition = POSITION_HEAD) to guarantee they execute before the IIFE; protocol-relative // URLs replaced with explicit https:// to satisfy CSP on localhost (May 2026)

Sonarcloud CLI — Setup local SonarCloud integration in VS Code (May 2026)

Sonarcloud Setup — Setup local SonarCloud integration (May 2026)

Sonarqube IDE with m.bat/Makefile — Sonarqube IDE Setup (May 2026)

SCSS Architecture — two independent SCSS trees (light and dark); full import chain from _yii3i_variables.scss through Bootstrap 5 source to _core.scss and _custom_styles.scss; how Bootstrap !default variable overrides work; file roles; when and how to rebuild compiled CSS (May 2026)

FontAwesome to Bootstrap Icons — complete removal of FontAwesome from the asset pipeline: ~1.1 MB of font files deleted, dead SCSS rules removed from _core.scss and _welcome.scss, compiled FA rules removed from style.css and utilities.css, $fa-font-path removed from _yii3i_variables.scss; Bootstrap Icons (bi bi-*) confirmed as sole icon library; outstanding dark-theme SCSS import noted (May 2026)

CSS Variables Reorganization — plan to break monolithic style.css into six purpose-specific files (variables.css, base.css, layout.css, components.css, utilities.css, overrides.css); current live vs. planning-stage status; remaining migration items; original source line-range mapping (May 2026)

PDF Bootstrap 5 Shimcustom-pdf.css Bootstrap 5 utility shim for mPDF replacing kv-mpdf-bootstrap.min.css; full BS5 class inventory (typography, spacing, tables, colour, borders); templates.css fixed (clearfix::after removed, :nth-child.odd/.even, th.text-end); all five PDF templates updated (text-end, m-0, item-table, visible <thead>, odd/even row shading); watermark src bug fixed in overdue.php; stray } removed from quote.php and salesorder.php (May 2026)

Lighthouse Performance Audit — How to run a Lighthouse audit from Chrome DevTools or the CLI against an authenticated page; performance score 68 → 95 via Apache compression modules, asset deduplication, CSS deferral, Amazon Pay JS conditionalisation, image resizing, and N+1 settings-query fix (May 2026)

Bootstrap 3 CSS Removal — incremental removal of InvoicePlane's legacy Bootstrap 3 styles from style.css (custom section reduced 32 %, 966 → 653 lines); 484 form-groupmb-3, 23 dropdown-buttondropdown-item, 12 input-smform-control-sm replacements across 120 view files; SonarCloud duplicate-selector warnings eliminated by excluding src/Invoice/Asset/**; .table { font-size: 0.25rem } bug fix (4 px invisible text); body *:focus { outline: none !important } removed (WCAG 2.1 accessibility) (May 2026)

Bootstrap 3 → Bootstrap 5 Migration Guide — PHP-community field guide documenting 16 categories of migration difficulty: class renames (grid, typography, buttons, forms, panels→cards, navbar, labels→badges, tables), data-attribute prefix change (data-data-bs-), JS API ($(el).modal()bootstrap.Modal.getOrCreateInstance(el)), mPDF CSS 2.1 limitations (no var(), no flexbox, no :nth-child), FontAwesome → Bootstrap Icons, Yii3 widget-layer BS3 class output, SCSS !default override order, input-group pitfall, SonarCloud false-positive suppression, and 10 things that surprised Claude most during the migration (May 2026)

Bootstrap 5 Table Mobile Stacking Fixtable, thead, tbody, th, td, tr { display: block } ported back into layout.css at @media (max-width: 767px); restores the vertical cell stacking that existed in the Bootstrap 3 era and was removed when BS3 CSS was stripped; td[data-label] scoping prevents 50 % padding gap on GridView cells that do not emit data-label attributes (May 2026)

Bootstrap 5 Tooltip Initialisation FixBootstrapJsOnlyAsset registered before InvoiceNodeModulesAsset so window.bootstrap is defined when the IIFE runs; dead DOMContentLoaded wrapper removed from initializeTooltips(); bare bootstrap identifier replaced with (window as any).bootstrap; new Tooltip() replaced with Tooltip.getOrCreateInstance() to prevent duplicate instances (May 2026)

Bootstrap 5 Settings Tabs & HTMX Page-Size Selector — BS5 tab accessibility pass on all settings partials (role="tablist/tab/tabpanel", aria-*); form-select applied to 16 partial files; 19 inline label style tags consolidated to overrides.css; page-size navbar buttons save via hx-get + hx-swap="none" then refresh #main-area via fetch+DOMParser+replaceWith without redirect or full reload (May 2026)

Global Page Size Navbar SelectorPageSizeLimiter widget removed from 27 views and 3 widget classes; replaced by a single <select> in the invoice layout navbar backed by a TypeScript PageSizeHandler; BootstrapJsOnlyAsset hash-collision fix; dark mode removed; CustomFieldRepository PSR-4 path fix (May 2026)

Onboarding — `Stacking Rule layout fix in src/Invoice/Asset/invoice/css/layout.css (May 2026)

Performance Benchmarks — custom hrtime() benchmark suite tracking Yii3's four core speed-critical components over the repo's lifespan: DI container (singleton cache, 5-level dependency chain), injector auto-wire (reflection-cache vs uncached), FastRoute URL matcher (50-route table, parametrised, worst-case, 404), and string helpers (StringHelper, Inflector, WildcardPattern, CombinedRegexp); results accumulate in benchmarks/results/history.json; interactive Chart.js dashboard with trend arrows, suite filters, run selector, and ops/sec bars; GitHub Actions records a run every Monday at 02:00 UTC with OPcache JIT enabled (May 2026)

FastRoute Dispatch CacheUrlMatcher PSR-16 cache wiring: CacheInterfaceFileCacheruntime/cache/routes-cache; cache disabled in dev via common/params.php, enabled in prod via environments/prod/params.php; YII_ENV environment variable drives which params file is loaded; new routes return 404 after git pull until rm -rf runtime/cache/* is run on the server — add to deploy script before PHP-FPM restart; benchmark context explaining why the Windows dev figures include compilation overhead that disappears in production (June 2026)

PHPUnit Entity Test Migration — 34 new PHPUnit entity tests across 6 batches; 36 Codeception unit tests migrated to PHPUnit\Framework\TestCase; 26 createMock() calls replaced with createStub(); 3 pre-existing DateTime/DateTimeImmutable entity bugs uncovered (May 2026)

Peppol SMP Lookup — participant discovery via SML DNS → SMP HTTP → XML parse; SmpResolver supports both PEPPOL SMP 1.0 and BDX SMP 1.0 namespaces; SmpEndpoint value object; PEPPOL_SML_ZONE and PEPPOL_SMP_BASE_URL env vars; 10-test PHPUnit suite; completes Phase 1 of the Peppol access point (May 2026)

HTMX CachingVary: HX-Request strategy for CDN/proxy caches; browser cache headers for XHR GET requests; why POST filter forms are exempt; Nginx cache-key configuration; current project status checklist (May 2026)

Peppol Send via Oxalis — end-to-end implementation of "Send via Peppol (Oxalis)" on the invoice view; PeppolMessage Cycle ORM entity; PeppolSendService PSR-18 HTTP wrapper; QUEUED→SENT→FAILED status lifecycle; WireMock Phase A dev setup; Yii3 DI config auto-loaded from OXALIS_BASE_URL env var (May 2026)

Peppol Oxalis Connect — Phase B real-Oxalis wiring: PeppolSendService switched from JSON to multipart/form-data; iso6523-actorid-upis:: and cenbii-procid-ubl:: scheme prefixes; PEPPOL_SENDER_ID env var; HTTP 4xx/5xx mapped to FAILED; PeppolInboundController delivery callback; DocumentTypeId busdox caveat; Phase B checklist (May 2026)

Oxalis Integration Plan — phased plan for self-hosted Peppol AS4 transport via Oxalis alongside the existing Storecove connector; cost comparison (managed AP vs. self-hosted); PeppolMessage state machine; PeppolSendService wrapping Oxalis REST API; inbound callback controller; SMP registration and OpenPeppol certification costs (May 2026)

Peppol Access Point PHP Guide — architectural overview for building a Peppol access point in PHP; AS4/WS-Security delegation strategy; recommended PHP libraries (xmlseclibs, sabre/xml); phased delivery from minimal outbound-only AP through full certification (May 2026)

HTMX Invoices List Widget — sort, filter, pagination, and group-by on the invoice list using HTMX 2.x; InvsListWidget wraps GridView with hx-boost; partial outerHTML swap of #InvsGridView; edit-column read-only/disable-read-only matrix; sent-log columns; group-by with paid/balance totals per group header; 44-test PHPUnit suite (May 2026)

HTMX Quotes List Widget — sort, filter, pagination, and group-by on the quote list using HTMX 2.x; QuotesListWidget wraps GridView with hx-boost; partial outerHTML swap of #QuotesGridView; group-by with collapsible headers; SonarQube S138/S3776/S107 refactoring (May 2026)

HTMX User Index — sort, pagination, and page-size selector on the user list using HTMX 2.x; UsersListWidget wraps GridView with hx-boost on sort and pagination links; partial outerHTML swap of #UsersGridView (May 2026)

HTMX Quote Item Entry — in-place product and task line item addition on the quote view using HTMX 2.x; dedicated QuoteItemHtmxController with quoteitemhtmx/addProduct and quoteitemhtmx/addTask POST-only routes; no full page reload; loading spinner with auto-reset on success; htmx 2.0.10 bundled into the TypeScript iife via npm (May 2026)

RBAC DB Storage — assignments migrated from resources/rbac/assignments.php to yii_rbac_assignment MySQL table via yiisoft/rbac-cycle-db; items remain PHP-file backed (May 2026)

Cycle ORM TransactionsInvService::withTransaction() wraps invoice create, credit, copy, and invoice-to-invoice confirm in atomic database transactions; orphaned rows on partial failure are no longer possible (May 2026)

Invoice Soft Delete & Trash — Trash page listing archived invoices with per-row restore; restore() method on Inv; explicit WHERE deleted_at IS NULL on all 48 InvRepository query methods; InvDeletionService removed; 17-test PHPUnit suite covering soft-delete and restore lifecycle (May 2026)

InvForm::show() Pattern — Bug fix: inv/view status dropdown always showed Draft because new InvForm() defaults status_id = 1; replaced with InvForm::show($inv) which copies all entity fields into the form (May 2026)

Cycle ORM Entity Behaviors — SoftDelete on Inv (audit-safe deletion) and Hook on Client (auto-sync client_full_name on create/update) (May 2026)

Cycle ORM Database Indexing — #[Index] attributes applied to Inv, Quote, SalesOrder, Product, Client, and Family entities; rules for choosing sort, filter, FK, and unique indexes (May 2026)

Family Drag-and-Drop Street Order — reorder streets for a cleaning run via native HTML5 drag-and-drop, persisted automatically on drop (May 2026)

Telegram Payment Providers — native Telegram invoicing via phptg/bot-api by Sergei Predvoditelev (vjik) (May 2026)

Adapting forms for DDD (April 2026)

Entity to Infrastructure Migration Process (April 2026)

Architecture Domain Application Infrastructure (April 2026)

Cycle-Orm Psalm Lifecycle Safe Entities (April 2026)

Language Flag Dropdown (April 2026)

Settings Tabs Improvements (April 2026)

Soletrader Layout Improvements (April 2026)

Sidebar Improvements (April 2026)

Eslint Sonarqube Build Session (March 2026)

Avoiding RBAC Mutation (March 2026)

Php 8.4 Alpine Setup (March 2026)

Future Peppol Mena (March 2026)

Future Peppol Nigeria (March 2026)

Future Peppol Republic of South Africa (March 2026)

Future Peppol UK (March 2026)

Mobile-Desktop Toggle Toolbar (March 2026)

ssl.conf explained (March 2026)

Why Apache? (March 2026)

Apache2 vs. Nginx (March 2026)

Vultr Alpine Security (March 2026)

WSL to Alpine Deployment — step-by-step guide for pulling updates from GitHub to a live Alpine/Apache2 server via WSL; git stash/pop workflow; file ownership (chown apache:apache); session save-path configuration; Psalm on server; SCP file transfer; deploy script; OAuth2 and RBAC debugging commands (May 2026)

Alpine Linux CVE-2026-31431 Remediation — local privilege escalation via algif_aead kernel interface; immediate mitigation (/etc/modprobe.d/disable-algif.conf); kernel upgrade from 6.12.49 to 6.18.29 via apk; OpenRC Apache restart commands; post-reboot verification (May 2026)

phpMyAdmin Vulnerabilities on Alpine (March 2026)

AuthController Production Environment Fix (March 2026)

Content Security Policy Updates.htaccess CSP for Stripe/Braintree/Amazon Pay (March 2026); PSR-15 ContentSecurityPolicyMiddleware replacing it with script-src 'self' (no unsafe-inline/unsafe-eval), DI-injected policy string, payment-provider extensibility via params.php; response to htmx CodeQL alerts #194/#195 (June 2026)

Email Setup for yii3i.online (March 2026)

Automerge Renovate's dependency updates if tests pass (Feb 2026)

Fraud Prevention Headers Bugfix (Feb 2026)

UK e-invoicing B2B/B2G 2029 (Jan 2026)

PeppolValidator Integration. (Jan 2026)

CreditNote Integration. (Jan 2026)

VitePress Integration. (Dec 2025)

Prometheus Integration. (Dec 2025)

Prometheus Menu Integration. (Dec 2025)

Sonar Cloud Setup. (Nov 2025)

SonarQube for IDE Setup — VS Code Connected Mode setup; Windows startup timeout fix; JVM heap tuning; token generation explained in plain English; connectionId mismatch pitfall; Windows Defender exclusions (May 2026)

SonarCloud CLIsonar-issues.php queries the SonarCloud API and prints all 4000+ issues in Psalm-style format with copyable file paths; filters by type, severity, PR, and hotspots; curl-based to bypass WAMP allow_url_fopen restriction; composer shortcuts included (May 2026)

Netbeans ↔️ Vs Code: Sync Guide. (Dec 2025)

Php Product Selection Workflow. (Dec 2025)

Security Commands. (Dec 2025)

Typescript Build Process. — IIFE bundle 134.6 KB (ES2024, esbuild); full function-by-function reference for all 21 source modules; Bootstrap Icons migration; icon-spin CSS animation replacing fa-spin (May 2026)

Typescript ES2023 Modernization. (Dec 2025)

Typescript ES2024 Modernization. (Dec 2025)

Typescript Go V7 Compatability Testing Guide. (Dec 2025)

Invoice Amount Magnifier using Angular. (Dec 2025)

Family Commalist Picker using Angular. (Dec 2025)

Cycle ORM HasOne and outerKey Issue. (Jan 2026)

Cycle ORM Join Optimization. (Jan 2026)

Cycle ORM Foreign Key Constraint Issue. (Jan 2026)

Netbeans IDE 25-28 Guide. (Dec 2025)

Tooltip Styles Configuration. (Jan 2026)

Stage 1: Suitability Assessment for VAT Test Suite

This section records the pre-conditions and steps required before a VAT quarterly submission can be tested end-to-end against the HMRC Making Tax Digital (MTD) API using the Developer Sandbox environment.

Critical: Fix Production URL Bug First

HmrcController::vatObligations() and HmrcController::vatReturnSubmit() currently call https://api.service.hmrc.gov.uk (production). A sandbox OAuth2 token is rejected at production endpoints. Before any VAT testing can succeed, those URLs must be switched to https://test-api.service.hmrc.gov.uk when operating in sandbox mode. DeveloperSandboxHmrc::setEnvironment() already exists for this purpose but is not yet wired through to the controller HTTP calls.

Step 1 — Register on the HMRC Developer Hub

  1. Go to https://developer.service.hmrc.gov.uk and sign in (or create an account).
  2. Create a new Sandbox application.
  3. Set the redirect URI to match DEVELOPER_GOV_SANDBOX_HMRC_API_CLIENT_RETURN_URL in .env (e.g. https://yii3i.online/callbackDeveloperGovSandboxHmrc).
  4. Copy the new client ID and client secret into .env:
    DEVELOPER_GOV_SANDBOX_HMRC_API_CLIENT_ID=...
    DEVELOPER_GOV_SANDBOX_HMRC_API_CLIENT_SECRET=...
    DEVELOPER_GOV_SANDBOX_HMRC_API_CLIENT_RETURN_URL=...
    

Step 2 — Subscribe to All Relevant APIs

Subscribe to every API below in the sandbox application. HMRC silently drops unsubscribed scopes from the token response, so subscribing to all now costs nothing and means the HmrcApiCatalogue can show the full grant without a second OAuth round-trip.

API Name Scope(s) Identifier Why Relevant
VAT (MTD) read:vat, write:vat VRN Core — obligations retrieval and VAT100 return submission (Boxes 1–9)
Self Assessment (Individual) read:self-assessment, write:self-assessment NINO Income and expenses for sole traders issuing invoices
Self-Employed Business read:self-employment, write:self-employment NINO Used in HmrcController::selfEmploymentBusinesses()
Business Details read:self-assessment NINO Business name and address on returns
Individual Calculations read:self-assessment, write:self-assessment NINO Tax calculation results
Income Received read:self-assessment, write:self-assessment NINO Invoice income classification
National Insurance Record read:national-insurance-record NINO NI contributions for self-employed
Customs Declarations write:customs-declaration EORI Import/export (future use)
Create Test User (sandbox utility) Required to generate sandbox VRN/NINO individuals via createTestUserIndividual()
Fraud Prevention Headers — Validate (sandbox utility) Used in HmrcController::fphValidate()
Fraud Prevention Headers — Feedback (sandbox utility) Used in HmrcController::fphFeedback()

Step 3 — What Happens After Subscribing

Once the sandbox application is subscribed and the OAuth2 flow is completed via /auth/authclient?authclient=developersandboxhmrc, the session stores hmrc_scope containing all granted scopes. HmrcController::index() then displays the full HmrcApiCatalogue with available APIs highlighted.

The VAT test sequence after that point is:

  1. createTestUserIndividual — creates a sandbox individual with a VRN and NINO; the sandbox auto-generates open quarterly obligations for that VRN.
  2. Store the VRN in Settings → VAT Registration Number.
  3. vatObligations() — retrieves open quarterly obligations for the VRN.
  4. vatReturnPrepare() — auto-fills Box 1 (output VAT) and Box 6 (sales ex-VAT) from InvAmountRepository::repoVatTotalsForPeriod(), and Box 4 (input VAT) and Box 7 (purchases ex-VAT) from PurchaseEntryRepository::repoVatTotalsForPeriod(). Boxes 3 and 5 are computed client-side (JS). Boxes 2, 8, and 9 require manual entry.
  5. vatReturnSubmit() — POSTs the nine-box returnData payload to the sandbox endpoint and receives a processingDate confirmation in the response.

Stage 2 Preview — PHPUnit Test Data Suite

Once the sandbox URL bug is fixed and credentials are configured, Stage 2 will add:

  • InvAmountRepository::repoVatTotalsForPeriod() PHPUnit tests — seed known invoices (status 2/3/4) within a fixed quarter and assert Box 1 and Box 6 totals.
  • Purchase entry fixtures — seed PurchaseEntry rows with known VAT amounts and assert Box 4 and Box 7 totals.
  • Edge-case coverage — zero-rated lines, invoices outside the quarter, draft invoices (status 1, excluded), and mixed VAT rates.

Feature Specifics

  • Cycle ORM Interface using Invoiceplane type database schema.
  • Generate VAT invoices using mPDF.
  • Code Generator - Controller to views.
  • PCI-compliant payment gateway interfaces – Braintree Sandbox, Stripe Sandbox, and Amazon Pay integration tested.
  • Generate OpenPeppol UBL 2.4 Invoice 3.0.15 XML invoices – validated with Ecosio.
  • StoreCove API connector with JSON invoice.
  • Invoice cycle – Quote to Sales Order (with client's purchase order details) to Invoice.
  • Multiple language compliant – steps to generate new language files included.
  • Separate Client Console and Company Console.
  • Install with Composer.
  • SonarQubeCloud / SonarCloud Code Analysis
  • NetBeans 28 && Vs Code IDE Integration
  • Eclipse IDE Integration
  • SonarLint4NetBeans Plugin - Tools ... Options ... Miscellaneous ... php ... Rules

Installing with Composer in Windows composer update

After a composer update, you'll need to manually:

  1. Set BUILD_DATABASE=true in your .env file
  2. Start the application to trigger table creation
  3. Reset BUILD_DATABASE= for better performance

Installing npm_modules

  • Step 1: Download node.js at https://nodejs.org/en/download
  • Step 2: Ensure C:\ProgramFiles\nodejs is in environment variable path. Search ... edit the system environment variables
  • Step 3: Run npm i in c:\wamp64\invoice folder. This will install @popperjs, Bootstrap 5, and TypeScript into a new node_modules folder.
  • Step 4: Keep your npm up to date by running, for example, npm install -g npm@10.8.1 or just npm install -g.

Rebuilding the TypeScript bundle (invoice-typescript-iife.js)

The compiled bundle at src/Invoice/Asset/rebuild/js/invoice-typescript-iife.js must be rebuilt whenever TypeScript source files change (including src/typescript/htmx.ts which bundles htmx 2.x). Run:

npm run build:typescript

Then copy the updated bundle to the Yii3-published assets directory so the browser receives the new file without a cache clear:

src/Invoice/Asset/rebuild/js/invoice-typescript-iife.js
  →  public/assets/<hash>/rebuild/js/invoice-typescript-iife.js

The <hash> folder name is derived from the asset source path and stays stable between builds — check public/assets/ for the existing folder name (e.g. 7246626a).

Recommended php.ini settings

  • Step 1: Wampserver ... Php {version} ... Php Settings ... xdebug.mode = off
  • Step 2: ... Maximum Execution Time = 360

Installing the database in mySql

  1. Create a database in mySql called yii3_i.
  2. The BUILD_DATABASE=true setting in the config/common/params.php file will ensure a firstrun setup of tables.
  3. After the setup of tables, ensure that this setting is changed back to false otherwise you will get performance issues.

The c:\wamp64\yii3-i\config\common\params.php file line approx. 193 will automatically build up the tables under database yii3-i.

'mode' => $_ENV['BUILD_DATABASE'] ? PhpFileSchemaProvider::MODE_WRITE_ONLY : PhpFileSchemaProvider::MODE_READ_AND_WRITE,

** If you adjust any Entity file you will have to always make two adjustments to** ** ensure the database is updated with the new changes and relevant fields: **

    1. Change the BUILD_DATABASE=false in the .env file at the root to BUILD_DATABASE=true
    1. Once the changes have been reflected and you have checked them via e.g. phpMyAdmin revert back to the original settings

Signup your first user using + Person icon. This user will automatically be assigned the admin role. If you do not have an internet connection you will receive an email failed message but you will still be able to login.

You or your customer, signup the second user as your Client/Customer. They will automatically be assigned the observer role. If you do not have an internet connection you will get a failed message but if your admin makes the 'Invoice User Account' status active the user will be able to log in.

If a user signs up by email, they will automatically be assigned as a client, and automatically be made active.

If your user has not signed up by email verification, to enable your signed-up Client to make payments:

  • Step 1: Make sure you have created a client ie. Client ... View ... New
  • Step 2: Create a Settings...Invoice User Account
  • Step 3: Use the Assigned Client ... Burger Button ... and assign the New User Account to an existing Client.
  • Step 4: Make sure they are active.
  • Step 5: Make sure the relevant invoice has the status 'sent' either by manually editing the status of the invoice under Invoice ... View ... Options or by actually sending the invoice to the client by email under Invoice ... View ... Options.

To install at least a service and a product, and a foreign and a non-foreign client automatically, please follow these steps:

  • Step 1: Settings ... View ... General ... Install Test Data ... Yes AND Use Test Date ... Yes
  • Step 2: In the settings menu, you will now see 'Test data can now be installed'. Click on it.

The package by default will not use VAT and will use the traditional Invoiceplane type installation providing both line-item tax and invoice tax

If you require VAT based invoices, ensure VAT is setup by going to Settings ... Views ... Value Added Tax and use a separate database for this purpose. Only line-item tax will be available.

Steps to translate into another language:

GeneratorController includes a function google_translate_lang ...
This function takes the English app_lang.php array auto generated in

src/Invoice/Language/English

and translates it into the chosen locale (Settings...View...Google Translate) outputting it to resources/views/generator/output_overwrite.

Xml electronic invoices - Can be output if the following sequence is followed:

  • a: A logged in Client sets up their Peppol details on their side via Client...View...Options...Edit Peppol Details for e-invoicing.

  • b: A quote is created and sent by the Administrator to the Client.

  • c: A logged in Client creates a sales order from the quote with their purchase order number, purchase order line number, and their contact person in the modal.

  • d: A logged in Client, on each of the sales order line items, inputs their line item purchase order reference number, and their purchase order line number. (Mandatory or else exception will be raised).

  • e: A logged in Administrator, requests that terms and conditions be accepted.

  • f: A logged in Client accepts the terms and conditions.

  • g: A logged in Administrator, updates the status of the sales order from assembled, approved, confirmed, to generate.

  • h: A logged in Administrator can generate an invoice if the sales order status is on 'generate'

  • i: A logged in Administrator can now generate a Peppol XML Invoice using today's exchange rates set up in Settings...View...Peppol Electronic Invoicing...One of From Currency and one of To Currency.

  • j: Peppol exceptions will be raised.

Renovate Auto-Merge Configuration

This repository uses Renovate Bot with auto-merge functionality enabled. The platformAutomerge is set to true, which enables GitHub's native auto-merge feature for Renovate pull requests.

Auto-Merge Requirements

IMPORTANT: Before any auto-merge occurs, all required checks must pass, including:

✅ Required Tests

  • Psalm Static Analysis - Must pass successfully
  • All other CI/CD pipeline tests must pass
  • Branch protection rules must be satisfied

How It Works

  1. Renovate creates a pull request for a dependency update
  2. GitHub's auto-merge is automatically enabled on the PR
  3. GitHub Actions/CI pipeline runs automatically
  4. Psalm static analysis tests are executed
  5. If Psalm and all other required checks pass ✅
    • GitHub automatically merges the PR to main
  6. If Psalm or any check fails ❌
    • The PR remains open
    • No auto-merge occurs
    • Manual review and fixes are required

Protection Mechanism

The auto-merge will NOT proceed if:

  • ❌ Psalm detects any type errors or issues
  • ❌ Any required status check fails
  • ❌ Branch protection rules are not met
  • ❌ Merge conflicts exist

This ensures that only dependency updates that pass all quality gates (including Psalm static analysis) are automatically merged to the main branch.

Configuration

The Renovate configuration in renovate.json includes:

{
    "$schema": "https://docs.renovatebot.com/renovate-schema.json",
    "extends": [
        "config:recommended"
    ],
    "platformAutomerge": true,
    "major": {
        "dependencyDashboardApproval": true
    }
}

The platformAutomerge: true setting leverages GitHub's native auto-merge functionality, working in conjunction with your branch protection rules and required status checks to maintain code quality.

Benefits

  • 🚀 Faster dependency updates
  • 🛡️ Protected by Psalm static analysis
  • ✅ Only merges when all tests pass
  • 🔒 Main branch remains stable
  • 🔄 Uses GitHub's native auto-merge feature

Additional Notes

Major version updates require manual approval via the Renovate Dependency Dashboard due to the "dependencyDashboardApproval": true setting for major updates.