feat(ksef): emit FA(3) Platnosc from connection payment config - #1317
Conversation
Adds the full implementation plan for emitting payment method, bank account, payment term, and skonto in KSeF FA(3) invoices as a per-connection config value (no live bank-accounts API on the KSeF side, unlike inFakt's #1303/#1308). Includes the schema-audited XSD child order for Platnosc (TerminPlatnosci -> FormaPlatnosci -> RachunekBankowy -> Skonto) and the design mockup referenced by the issue. Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
Adds a per-connection, manually-entered payment configuration (default payment method, bank account, payment term, early-payment discount) and emits it into the FA(3) Platnosc element whenever configured. Unlike inFakt (#1303/#1308), KSeF has no live bank-accounts API, so this is a plain config value the operator types in once, mapped straight through the existing seller/defaultTaxRate resolution chain (factory -> adapter -> mapper -> pure builder). - KsefPaymentConfig / KsefBankAccountConfig / KsefFormaPlatnosciValues on KsefConnectionConfig (domain types) - Fa3PaymentInput / Fa3BankAccount / Fa3FormaPlatnosciValues (FA(3) builder-internal types) - platnoscNode() in fa3-xml.builder.ts, emitted as a sibling of FaWiersz in the XSD-mandated child order: TerminPlatnosci -> FormaPlatnosci -> RachunekBankowy -> Skonto (confirmed against the vendored FA(3) v1-0E XSD - not payment-method-first) - KsefAdapterFactory.resolvePayment + shape-validator checks for formaPlatnosci / bankAccount.nrRb / paymentTermDays - FE: ksef-payment-config.ts assembly module (mirrors ksef-seller-config.ts) wired into edit-connection.schema.ts, new fields in ksef-structured-section.tsx - FA3_IMPLEMENTATION_NOTES.md updated with the Platnosc mapping table Backend: 305/305 ksef unit tests pass, including full XSD structural validation of the new Platnosc block for both configured and unconfigured connections. Frontend: 1887/1887 web unit tests pass. check:invariants clean. Closes #1311 Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
Live Playwright smoke test against a real dev stack + KSeF sandbox connection (Phase 6 of the implementation plan) surfaced a real bug: applyKsefPaymentToConfig deleted the whole bankAccount/skonto sub-object whenever a sibling field (nrRb, or the other of conditions/amount) wasn't present in the SAME merge call. Since each field syncs to configText independently per keystroke, this silently discarded whatever the operator typed first - skonto could never actually be saved, and a bankAccount sub-field typed before nrRb was lost. Fix: only drop a sub-object when it is completely empty (no keys at all), never based on a missing sibling. The "nrRb required if bankAccount is set" and "skonto needs both conditions+amount" rules now live where they belong - the backend shape validator (save time, new skonto check added) and the factory's resolvePayment (issuance time) - so a violation surfaces as a clear error instead of silent data loss. Adds the e2e smoke-test script (apps/web/e2e/ksef-payment-config.mjs) and its screenshots (docs/assets/ksef-1311-smoke/) used to find this and verify the fix + full field set against the design mockup. Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
norbert-kulus-blockydevs
left a comment
There was a problem hiding this comment.
Tech Lead Review — PR #1317 (KSeF FA(3) Platnosc)
Summary
Solid, well-scoped PR. Everything stays inside libs/integrations/ksef and its FE plugin slice (ADR-026 compliant — no CORE changes), the factory→adapter→mapper→builder wiring mirrors the existing resolveSeller/resolveDefaultTaxRate chain exactly, naming/typing conventions (as const unions, *.types.ts, file headers) are followed, and test coverage is thorough (builder XSD-order cases, mapper, adapter, factory, shape validator, FE assembly + component). No architecture or CORE/Integration boundary violations found. Two SUGGESTION-level gaps below, neither blocking.
Issues
[SUGGESTION] — libs/integrations/ksef/src/infrastructure/adapters/ksef-connection-config-shape-validator.adapter.ts (payment.bankAccount.nrRb check) / apps/web/src/features/connections/components/edit-connection.schema.ts (paymentBankAccountNrRb schema)
The implementation plan documents
NrRBasTNrRB, 10–34 chars per the XSD pattern (§4), but neither the shape validator nor the FE Zod schema enforce a minimum length — only non-emptiness (BE) / a max of 34 (FE). An operator could save an obviously truncated account number (e.g. 3 digits) that passes both client and server validation, only to fail at actual KSeF submission time with an opaque XSD error far removed from the config-save screen where the mistake was made. Sinceengineering-standards.md's validation guidance is to reject bad input at the boundary rather than let it surface downstream, consider adding a length/pattern check mirroring the XSD (10–34chars) to the shape validator, with the FE schema following suit (.min(10)).
[SUGGESTION] — libs/integrations/ksef/src/application/factories/ksef-adapter.factory.ts:168-193 (resolvePayment)
The method's doc comment claims a defensive posture "mirroring
resolveDefaultTaxRate's defensive posture," and it does re-validatebankAccount(drops it ifnrRbis falsy) andskonto(drops it unless both sub-fields are present) — butformaPlatnosciandpaymentTermDaysare passed through unchecked (result.formaPlatnosci = payment.formaPlatnosci;/result.paymentTermDays = payment.paymentTermDays;). In the normal flow the shape validator already rejects an out-of-rangeformaPlatnoscior a negative/non-integerpaymentTermDaysat save time, so this is low-impact — but for a connection whose config predates the validator (or was written through another path), an invalid code/negative day-count would be emitted verbatim intoFormaPlatnosci/TerminOpis/Iloscrather than being defensively dropped like the other two fields. Minor inconsistency worth a one-line guard (or a code-comment caveat) if the "defensive" framing is meant to be complete.
Verdict
✅ Approve — both findings are non-blocking robustness suggestions; nothing here violates hexagonal boundaries, naming conventions, or the documented ADR-026 country-agnostic-core rule.
…11-ksef-platnosc-plan
All acceptance criteria satisfied and Phase 6 (live smoke test + verification artifact) done - link to the published artifact and note the persistence bug found/fixed during the smoke test. Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
- Shape validator + FE Zod schema now enforce the FA(3) TNrRB length bound (10-34 chars) on payment.bankAccount.nrRb instead of only non-emptiness, so a truncated account number is rejected at connection-save time rather than surfacing as an opaque KSeF XSD error at issuance. - resolvePayment now defensively drops an unknown formaPlatnosci code or a negative/non-integer paymentTermDays (mirroring the existing bankAccount/skonto guards) for connections whose config predates the ksef.publicapi.v2 shape validator. Addresses both SUGGESTION findings from the tech-lead review on #1317. Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
|
Addressed both SUGGESTION findings from the tech-lead review in 6d29c91:
Added corresponding unit tests in both the KSeF integration package and the web edit-connection schema. |
…NazwaBanku) Confirmed live against the KSeF sandbox: the FA(3) XSD mandates NrRB before SWIFT before NazwaBanku inside RachunekBankowy. Emitting NazwaBanku before SWIFT caused KSeF to reject the invoice (status 450, "invalid child element SWIFT... expected OpisRachunku"). Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
Two E2E-verified prerequisites for live KSeF acceptance, confirmed against the KSeF test environment while doing live sandbox verification of #1311 (the Platnosc feature was silently blocked from ever actually being accepted end-to-end without these): 1. AES session IV: encryptDocument was generating a fresh random IV per document, but KSeF's SendInvoiceRequest wire shape has no per-document IV field — it decrypts every document in a session using the session IV declared once in OpenOnlineSessionRequest.encryption.initializationVector. A per-document IV caused a SHA-256 hash mismatch (status 430) because KSeF's decryption produced garbage. Fixed: encryptDocument now reuses context.symmetricKey.iv (the session IV) for every document. 2. JST and GV are REQUIRED by the FA(3) XSD on Podmiot2 (no minOccurs="0") but were never emitted, causing a semantic validation rejection. Fixed: buyerNode now always emits JST=2, GV=2 ("nie dotyczy" — not a JST subsidiary unit / not a VAT group member). Cherry-picked from 634c27a5 (previously only on the unmerged branch 1228-ksef-fa3-full-visualization-new, which was never pushed to origin) — without this fix no live KSeF invoice submission from this codebase can be genuinely accepted, regardless of Platnosc content. Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
piotrswierzy
left a comment
There was a problem hiding this comment.
Reviewed against the vendored schemat_fa3_v1-0e.xsd in the repo. The wiring is clean and idiomatic — mirrors resolveSeller/resolveDefaultTaxRate end-to-end, no CORE changes (ADR-026 clean), as const unions, types in *.types.ts, no any, and the config-merge fix + validator prototype-safety both check out. I verified most XSD claims and they hold: the Platnosc position under Fa (after FaWiersz/Rozliczenie, both optional), the TerminPlatnosci → FormaPlatnosci → RachunekBankowy → Skonto order, the TerminOpis complex {Ilosc, Jednostka, ZdarzeniePoczatkowe} shape, and the TFormaPlatnosci 1–7 values are all correct.
One blocking issue and one process gap.
🔴 BLOCKING — RachunekBankowy emits NazwaBanku before SWIFT (XSD-invalid)
rachunekBankowyNode in fa3-xml.builder.ts builds keys as NrRB → NazwaBanku → SWIFT, but the vendored XSD TRachunekBankowy sequence is:
NrRB, SWIFT (inner sequence)
RachunekWlasnyBanku?
NazwaBanku?
OpisRachunku?
SWIFT must come before NazwaBanku. When a connection has both bankName and swift set, the emitted document is XSD-invalid and KSeF clearance would reject it. Single-field paths (NrRB only, NrRB+swift, NrRB+bankName) are fine — only the both-set path breaks.
Fix: emit SWIFT before NazwaBanku in rachunekBankowyNode, and update fa3-xml.builder.spec.ts:485 — it currently asserts the wrong order (<NrRB><NazwaBanku><SWIFT>), which is why the bug reads as "passing."
🟠 IMPORTANT — the structural validator doesn't cover Platnosc, so the "validator passes" tests are false comfort
validateFa3Xml only checks root/namespace/Naglowek/Podmiot/Fa-required-children/FaWiersz — it never inspects Platnosc or child ordering. The two should pass the structural FA(3) validator … tests would (and do) pass even with the bug above. Combined with the blocked live sandbox submission, element-order correctness rests entirely on manual XSD reading plus tautological "emits-what-it-emits" regex assertions.
Please add a real order check for the Platnosc block (a targeted structural assertion, or an opt-in xmllint/libxmljs conformance pass in CI) so ordering regressions like the RachunekBankowy one are actually caught rather than co-signed by the test suite. This is the reason the bug slipped through.
🟡 SUGGESTIONS (non-blocking)
- The new
paymentconstructor arg onKsefInvoicingAdapteris inserted before the pre-existing defaultednowparam. Both internal call sites were updated so nothing breaks, but a mid-list positional insert is fragile — appending or an options object would be safer. - The "required-together" invariants now live in FE assembly + shape validator + factory
resolvePayment. Fine as defense-in-depth (matches theresolveSellerprecedent), just noting the triplication.
Everything else — the merge-bug fix (sub-objects dropped only when fully empty, required-together moved to validator + factory), neutrality, and typing — looks good. Fixing the RachunekBankowy order + its test, and closing the validator coverage gap, clears this for merge.
… + ctor options bag - validateFa3Xml now enforces the XSD-mandated child order of Fa/Platnosc (TerminPlatnosci -> FormaPlatnosci -> RachunekBankowy -> Skonto) and of each RachunekBankowy (NrRB required, SWIFT before NazwaBanku), so an ordering regression like the pre-review RachunekBankowy bug is caught by the 'passes the structural validator' tests instead of co-signed by them - KsefInvoicingAdapter trailing optional params (payment, now) move into a KsefInvoicingAdapterOptions bag so future additions never shift positional call sites - resolvePayment doc comment now names the intentional three-layer (FE assembly / shape validator / factory) defense-in-depth explicitly The blocking SWIFT/NazwaBanku emit-order finding was already fixed in c315e4d; this adds the validator coverage that would have caught it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
…c fix The merge helper intentionally persists an incomplete skonto (conditions without amount) so independent per-field config sync never drops the first-typed field; completeness is enforced at save/issuance time (#1311 smoke-test finding). The test still asserted the old drop-on-incomplete behaviour. Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
…1328) Semantic merge conflict between #1316 (URI versioning, global /v1 default) and #1313 (new listings-invalid-path-id.int-spec.ts written pre-versioning): the spec's unprefixed /listings/... requests stopped matching any route, so every request returned the router-level 404 ("Cannot GET ...") - the three expect(400) ParseUUIDPipe assertions failed and the expect(404) ones passed vacuously. Main has been red on Integration Tests since the two merged. Prefix the five request paths with /v1, matching every other listings int-spec. Verified against the Testcontainers harness: 5/5 pass. Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
|
@piotrswierzy re: your review (#1317 (review)) - all findings addressed:
Why PR #1328 was merged into this branch instead of main: main's Integration Tests job has been red since #1316 (URI versioning, global CI is fully green on the current head (including Integration Tests). Ready for re-review. |
Tech review (Claude, /tech-review)SummarySolid, well-layered implementation of #1311. The Issues[IMPORTANT] -
[IMPORTANT] -
[SUGGESTION] -
[SUGGESTION] -
[SUGGESTION] -
What's good
Verdict🔄 Approve with changes - mergeable after the two IMPORTANT items (document the bundled IV/JST-GV/int-spec changes in the PR body; normalize the NRB input). |
- NrRB input is whitespace-stripped at FE assembly time (new normalizeNrRb, mirroring the normalizeNip precedent) so a conventionally-spaced NRB paste never reaches config.payment.bankAccount.nrRb or the FA(3) wire with inner spaces; the FE zod length check now counts the stripped value, converging with the BE shape validator's 10-34 bound - shape validator rejects a wrong-typed payment / bankAccount / skonto with an explicit 'must be an object' issue instead of silently falling through to issuance-time drop - the tripled '1'..'7' TFormaPlatnosci list now carries a cross-reference comment at all three declaration sites (FE schema, plugin connection-config types, FA3 schema types) so a future 8th code is added everywhere - ksef-payment-config.mjs smoke script reads login credentials from WEB_USER / WEB_PASSWORD env vars (dev-stack defaults preserved), matching the WEB_BASE / KSEF_CONN_ID pattern Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
|
Re: the second tech-review round (2 IMPORTANT + 3 SUGGESTION) - all findings addressed in 3a960dc + a PR-body update:
Verified: |
Tech review summarySummaryWell-executed, self-contained KSeF feature. The FA(3) Issues[IMPORTANT] [IMPORTANT] [SUGGESTION] Bundled changes - the session-IV fix and JST/GV fix were found by this PR's own live E2E and are well-documented in the body, so bundling is acceptable; the [SUGGESTION] KSeF-specific assembly keeps accumulating in the shared [SUGGESTION] Notable positives
Verdict🔄 Approve with changes - fix the nrRb whitespace gap in the shape validator and record the JST/GV limitation; everything else is optional. |
- shape validator now rejects an nrRb containing inner whitespace (the FE strips via normalizeNrRb, but a direct API write bypassed that and a spaced NRB would fail KSeF's TNrRB pattern at clearance); the factory's resolvePayment additionally strips whitespace defensively for pre-validator config rows, and drops a whitespace-only value instead of emitting an empty NrRB - the hardcoded Podmiot2 JST/GV = 2 limitation is now recorded in FA3_IMPLEMENTATION_NOTES.md under Known limitations, with a pointer comment at the buyerNode declaration site - new drift spec makes the tripled TFormaPlatnosci '1'..'7' list self-enforcing: the two in-package arrays are compared by import and the FE array is extracted from its source file, so a one-sided edit fails the suite - follow-up issue #1330 filed for moving plugin-specific connection-config assembly behind a plugin slot Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
|
Heads-up: stacked PR #1334 (refactor(web): move plugin-specific connection-config assembly behind a plugin slot) has been merged into this branch, so its diff is now part of this PR. Why it lands here rather than as a separate PR to main: the refactor extracts the KSeF payment/seller connection-config assembly (introduced by this PR) out of the shared connections feature and behind a generic plugin slot. It builds directly on files added here (ksef-payment-config.ts, ksef-seller-config.ts, edit-connection schema wiring), so it could not target main independently without conflicting with this branch. Verification after the merge (stacked branches do not trigger CI, so this was checked locally on the merged tree): web type-check clean, all 1977 web unit tests green, web lint 0 errors, and the updated check-ksef-forma-platnosci-drift.mjs passes against the moved file path. The branch still merges cleanly into main. |
Tech Lead Review — #1317SummaryHigh-quality, well-documented PR. The KSeF Issues[SUGGESTION] — PR scope (whole PR)
[SUGGESTION] —
[SUGGESTION] —
[SUGGESTION] —
Nothing else surfaced: the CORE/Integration boundary is respected (no Verdict✅ Approve |
- Record the AES-256-CBC session-IV determinism trade-off under Known limitations in FA3_IMPLEMENTATION_NOTES.md, next to the JST/GV entry, so security reviewers find it where they look first. - Harden the TFormaPlatnosci drift script's extraction: tolerate an optional type annotation, Prettier multi-line wraps, and inline comments inside the array body. - Promote the untyped-bag string readers (readConfigString / readOptionalConfigString) to shared/plugins; ksef-connection-config and EditConnectionForm now share one definition instead of per-file copies. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
|
Re: the fifth tech-review round (4 SUGGESTION, 0 IMPORTANT) - all findings addressed in 36211ee:
Verified on the new head: web |
…ld-cache Ryuk is disabled on the persistent self-hosted runner, so the manual orphan-sweep step is the only cleanup for Testcontainers resources. docker rm -f (no -v) left anonymous volumes behind forever, and the sweep never touched dangling images or build cache — both accumulate indefinitely and were the actual cause of "no space left on device" integration-test failures unrelated to any test's own code. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
The prior sweep (docker rm -fv + filtered volume/image/builder prune) still failed with the exact same "no space left on device" error, and logged 0B reclaimed across all three prune calls. Two concrete bugs: - The volume-prune label filter never matches: anonymous volumes don't inherit the labels of the container that created them, so `--filter "label=org.testcontainers=true"` matches nothing. Dropped the filter — an unfiltered `docker volume prune` is still safe, since Docker refuses to remove a volume still attached to any container regardless of label. - Plain `docker image prune -f` only removes dangling (untagged) images; 0B reclaimed means this runner has none, so the real disk pressure is old-but-tagged image versions across many past integration-test pulls. Widened to `-af` (all unused images) — still safe under concurrency since Docker never removes an image a live container references, and a concurrent job needing a pruned image just re-pulls it rather than failing. Also logs `docker system df -v` before/after cleanup so the next failure (if any) can be diagnosed from CI output alone — there is no shell access to this runner. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
docker system df on the failing run revealed this self-hosted runner is shared with unrelated third-party workloads (linux-runner-1..4, hedera-payment-db, hawser-dockhand-agent — none of it OpenLinker's). The runner's disk pressure is caused by that other tenancy's live containers, not by anything OpenLinker's CI leaves behind, so a wider prune (image -af, unfiltered volume prune, builder prune) only ever reclaims OpenLinker's own garbage and can't fix a permanently oversubscribed shared host — while carrying real risk of touching image/volume state a co-tenant relies on. Reverting to the original, narrowly-scoped cleanup: remove only OpenLinker's own orphaned, Testcontainers-labeled containers whose owning CI run has finished. Disk capacity itself needs to be resolved with whoever operates this runner, not from this repo's workflow. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
…#1321) A full apps/api integration run boots 4 suite-scoped PrestaShop+MySQL Testcontainer pairs sequentially (maxWorkers: 1), and Ryuk is disabled on the self-hosted CI runner — so a suite that times out before reaching its own cleanup() leaks disk for the rest of that run with no way to tell, from CI output alone, which suite did it (a prior "no space left on device" mid-run failure had exactly this shape). Logs `docker system df` before each boot and after each cleanup() in the shared prestashop-container.helper.ts, so the next such failure can be attributed to a specific suite without runner shell access. Considered also converting the two PS suites that don't install the OL module (prestashop-harness-smoke, prestashop-webhook-provisioning) to mock the port instead of booting a real container, to cut boot count from 4 to 2. Both are deliberately real-PS regression guards — the smoke test exists to prove the Testcontainer harness itself works end-to-end, and the webhook-provisioning spec guards #541, a bug that was green at the unit-test/mock layer and only caught against the real WS API. Mocking either would remove the exact coverage they exist for, so this is diagnostics-only; reducing real PS boot count needs a different approach (e.g. sharing one boot between the two suites that already require installOlModule: true). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
…nt dirs (#1321) The PS test harness bind-mounts a mkdtemp dir onto /var/www/html and PS writes ~1GB into it as root; the unprivileged rmSync cleanup leaks it on the persistent self-hosted host, invisible to docker system df and the container sweep. Temporary read-only probe to confirm before fixing the cleanup path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
…1321) PS writes ~1GB into the ol-ps-data-* bind-mount as root, and on the runner-in-docker host the source path resolves on the HOST daemon's /tmp - the unprivileged rmSync cleanup could never delete it, so every PS suite leaked ~1GB onto the host root fs until containerd hit "no space left on device". Two-part fix: - removePsDataDir empties the dir through a root alpine container (works in both host topologies), then rmSync's the local dir. - The CI orphan-sweep step reaps host /tmp/ol-ps-data-* dirs older than 90 min (same age gate as the container sweep), covering SIGKILLed/cancelled runs that never reach in-process cleanup. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
…1321) Restores ci.yml to main's version. With the host root fs at 100% every docker-cli invocation in these steps hangs or fails, blocking the job before tests even start. The in-process removePsDataDir fix in the PS container helper stays - it adds no CI step. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
…ner boot (#1321)" This reverts commit eea68ac. Diagnostics no longer needed - the root cause (leaked root-owned ol-ps-data-* bind-mount dirs on the host) is identified and fixed by removePsDataDir. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
…rrections (#1297) (#1329) * docs(invoicing): implementation plan for issuance-time line snapshot (#1297) Plan for persisting a neutral issuance-time line snapshot on InvoiceRecord so KSeF (and any complete-resubmit) corrections diff against the lines as issued, not the order's current state. Branch cut from main (not #1317 - zero file overlap, snapshot is pure-core in InvoiceService). Refs #1297 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * feat(invoicing): persist issuance-time line snapshot for safe corrections (#1297) Persist a neutral issuedLineSnapshot ({ buyer, currency, lines }) on InvoiceRecord whenever a document is issued (from the issue command) or corrected (the correction's own post-correction lines), captured in the core InvoiceService - no adapter change. The correction endpoint now assembles originalDocument from that snapshot on the document being corrected, so a KOR's originalLineNumber-indexed deltas diff against the lines AS ISSUED (and, for a correction-of-correction, against the prior correction's own lines) even if the order changed since issuance. Records issued before the column existed fall back to the pre-#1297 order-derived reconstruction. - New IssuedLineSnapshot type + nullable jsonb column + migration 1818000000003. - InvoiceService.issueInvoice/issueCorrection populate it via InvoiceOutcomePatch; applyCorrectionDeltas computes the correction's after-lines from the original snapshot + per-line deltas. - Controller prefers the persisted snapshot (buildSnapshotFromRecord), skipping the order fetch; falls back to order-derived only when absent. - Not exposed via any response DTO (carries buyer PII), mirroring documentContent/sourceDocument. Tests: service snapshot on issue + 4 correction cases; controller snapshot- preference + correction-of-correction; repository jsonb round-trip int-spec. Closes #1297 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * refactor(invoicing): address tech-review suggestions on line snapshot (#1297) - Reject duplicate originalLineNumber correction lines at the HTTP boundary (new UniqueOriginalLineNumbersConstraint + DTO spec) so the persisted "after" snapshot can never silently last-write-win against what the provider computed; applyCorrectionDeltas doc notes the residual duplicate semantics for non-API callers. - Type IssuedLineSnapshot.buyer as the structural IssuedSnapshotBuyer shape instead of the BuyerProfile class - the field round-trips through jsonb without the class prototype, so the type now says so. - Document the transitional taxId: null caveat on buildSnapshotFromRecord for snapshots seeded by a correction of a pre-#1297 record (one-hop degradation that self-heals). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * refactor(invoicing): non-null-assert guarded fields in correction snapshot Per PR review: buildSnapshotFromRecord's `?? ''` / ternary `''` fallbacks for documentNumber/issueDate were dead branches masking the caller's existing non-null guard (providerInvoiceNumber/issuedAt asserted before this path runs). A future guard regression would now surface as a type error / crash instead of silently producing an invalid empty string. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> --------- Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… tag recipe (#1339) Pre-merge fixes for the 0.1.0 release-please baseline, from the PR #1332 review: - release-please-config.json: bump bootstrap-sha to the current main tip (e629837) so the first post-merge run stays dormant instead of opening a 0.2.0 Release PR that double-credits work the curated 0.1.0 section already describes (#1309/#1317/#1329/#1320/#1331). - CHANGELOG.md + RELEASING.md § Versioning policy: reword the pre-1.0 bump semantics to match the shipped config (minor = features + breaking, patch = fixes; bump-patch-for-minor-pre-major is false, so feat: bumps minor). RELEASING.md now cites the config keys so the two can't silently drift apart again. PUBLIC_API.md's "patch = additive" convention is intentionally untouched — that is the separate npm package axis (Changesets, deferred), not the product line. - RELEASING.md v0.1.0 recipe: tag the bootstrap-sha commit explicitly instead of the main tip at tag time — once a v0.1.0 tag + Release exist, release-please parses from that tag's commit, so tagging later silently drops commits from the generated 0.2.0 changelog. - release-please.yml: pin googleapis/release-please-action by commit SHA (v4.4.1) — the workflow holds contents+pull-requests write. Also merges origin/main (e629837) to bring the branch current. Refs #1332 Closes #1339 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Piotr Swierzy <piotr.swierzy@blockydevs.com>
- merge origin/main so the branch carries the shipped FA(3) Platnosc feature (#1317, b40902e) that the payment docs describe - the docs no longer reference an unmerged feature - repoint 4 inbound links broken by the docs/integrations -> package docs/ move (architecture-overview, getting-started, user-guide 02/04) - Erli README: capability row now matches ErliOfferManagerAdapter's implements list (drop OfferLister/OfferQuantityBatchUpdater, add OfferStatusReader/OfferStockRestorer/TaxonomyBorrower) - align the Part 2a heading dash with sibling headings - woocommerce-walkthrough.mjs: admin password from OL_ADMIN_PASSWORD env instead of a hardcoded shared-instance value Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
Repairs the BLOCKING merge-state defect and all IMPORTANT/SUGGESTION findings from the /pr-review on #1335. BLOCKING — the branch had merged an older main and, via a bad conflict resolution, silently reverted three shipped features (#1297 issued-line snapshot, #1317 KSeF FA(3) Płatność, #1330 connection-config plugin slot, plus release-please plumbing). Reset the branch to origin/main and re-applied only the Subiekt-scoped delta on top, so the diff is now the Subiekt-only change and no merged work is reverted. IMPORTANT 1. Payment/bank/cash-register config now rides the #1330 ConnectionConfigContribution plugin slot (new plugins/subiekt/subiekt-connection-config.ts) instead of growing the host edit-connection.schema.ts. 2. Payment method is a real tri-state: an explicit "Not set (Subiekt default)" option, and the summary derives from the actual state (no false "Cash" for unset). 3. use-set-default-bank-account-mutation now invalidates the owner-aware subiektBankAccounts key too, so isDefault flags don't go stale. 4. The section fires the default-sync via .mutate() (not void mutateAsync) to avoid an unhandled promise rejection on failure. 5. The set-default error toast copy is now provider-neutral (was inFakt). 6. Removed the stale "does not type-check (3-arg constructor)" comment in subiekt-adapter.factory.ts — the 4-arg constructor ships here. SUGGESTIONS - paymentFields() warns when transfer is configured without a bankAccountId. - setDefaultBankAccount guards the Number(accountId) coercion, throwing the config domain error on a non-numeric id. - Payment labels routed through t(); the cash-register help default is English. - Bank accounts group by the stable ownerPodmiotId, not the display name. Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Repairs the BLOCKING merge-state defect and all IMPORTANT/SUGGESTION findings from the /pr-review on #1335. BLOCKING — the branch had merged an older main and, via a bad conflict resolution, silently reverted three shipped features (#1297 issued-line snapshot, #1317 KSeF FA(3) Płatność, #1330 connection-config plugin slot, plus release-please plumbing). Reset the branch to origin/main and re-applied only the Subiekt-scoped delta on top, so the diff is now the Subiekt-only change and no merged work is reverted. IMPORTANT 1. Payment/bank/cash-register config now rides the #1330 ConnectionConfigContribution plugin slot (new plugins/subiekt/subiekt-connection-config.ts) instead of growing the host edit-connection.schema.ts. 2. Payment method is a real tri-state: an explicit "Not set (Subiekt default)" option, and the summary derives from the actual state (no false "Cash" for unset). 3. use-set-default-bank-account-mutation now invalidates the owner-aware subiektBankAccounts key too, so isDefault flags don't go stale. 4. The section fires the default-sync via .mutate() (not void mutateAsync) to avoid an unhandled promise rejection on failure. 5. The set-default error toast copy is now provider-neutral (was inFakt). 6. Removed the stale "does not type-check (3-arg constructor)" comment in subiekt-adapter.factory.ts — the 4-arg constructor ships here. SUGGESTIONS - paymentFields() warns when transfer is configured without a bankAccountId. - setDefaultBankAccount guards the Number(accountId) coercion, throwing the config domain error on a non-numeric id. - Payment labels routed through t(); the cash-register help default is English. - Bank accounts group by the stable ownerPodmiotId, not the display name. Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… Subiekt nexo (#1284) * docs(integrations): add README + operator tutorials for all 8 adapter packages Add per-package README.md to every integration package that was missing one: ai, allegro, dpd-polska, erli, inpost, ksef, subiekt, woocommerce. Each README covers adapter key + capabilities, credentials/config shape, and links to further operator docs. Add full A-to-Z operator tutorials for KSeF and Subiekt nexo with per-step screenshot placeholders (ksef/tutorial.md, subiekt/tutorial.md). The Subiekt tutorial covers bridge setup via PowerShell/WSL (without-exe-packaging branch), wizard, B2B faktura, B2C paragon, and idempotency. The KSeF tutorial covers token generation on the MF portal, connection wizard, B2B order, issuance, and UPO download. Create libs/integrations/ksef/assets/ and libs/integrations/subiekt/assets/ directories for future screenshot captures. Update root README Integrations table to link the tutorial.md files for KSeF and Subiekt nexo. Closes #1265 Closes #1266 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014NQB4zBWSrneR71TRKkx1t Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(integrations): add real Playwright screenshots + rewrite KSeF and Subiekt tutorials Replaces all placeholder screenshot references in KSeF and Subiekt nexo tutorials with actual Playwright captures taken against a live OpenLinker instance (preview build with all in-flight FE PRs merged). KSeF assets (14 PNGs): 01-02: connections list + platform picker 03-09: wizard fields (name, env, NIP, address, auth-type, secret) 10-12: connection created, list, detail page 13-14: invoices list with regulatory badges, invoice detail Subiekt assets (18 PNGs): 06-15: connection wizard (empty -> filled -> created -> test -> list -> detail) 20-26: invoice flow (orders list, order detail, connection picker, ready-to-issue, issue clicked, issued state, invoices list) Tutorial rewrites: - ksef/tutorial.md: reordered around actual wizard fields; Part 1 (KSeF portal) marked manual; Parts 2-5 use real screenshot refs - subiekt/tutorial.md: full rewrite to match connection-picker flow (multiple Invoicing connections), real bridge startup instructions, TLS note, idempotency Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014S9kgctitB75BfHEDejJ6B Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(subiekt): add real Subiekt nexo FS document screenshot to tutorial Part 5 (verify in Subiekt nexo) was the only remaining manual-step placeholder without a real capture. Replaces it with an actual screenshot of an issued FS document open in Subiekt nexo desktop. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014S9kgctitB75BfHEDejJ6B Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(ksef): add real KSeF 2.0 test-portal screenshots for token generation Part 1 (get a KSeF authorisation token) was the last manual-step placeholder. Captures the full flow on the live ap-test.ksef.mf.gov.pl portal: test-auth login, NIP + test certificate, dashboard, token list, generate-token form, and the revealed token (value redacted before commit since it's a live, usable test-environment credential). Also corrects the test-portal URL: ksef-test.mf.gov.pl (KSeF 1.0) was decommissioned 2025-09-01; the current test environment is ap-test.ksef.mf.gov.pl (KSeF 2.0). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014S9kgctitB75BfHEDejJ6B Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(subiekt): point to the openlinker-subiekt-bridge repo The bridge is being moved to its own repo under the openlinker-project org (not yet published). Update README + tutorial repo links and clone paths accordingly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014S9kgctitB75BfHEDejJ6B Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(integrations): move all integration docs + assets under libs/integrations/<pkg>/docs/ Establishes one consistent convention across all 8 integration packages: docs (setup guides, runbooks, tutorials) and their image assets live at libs/integrations/<pkg>/docs/ and libs/integrations/<pkg>/docs/assets/, co-located with the code they document instead of split across a separate docs/integrations/<pkg>/ tree. Moves (via git mv, history preserved): - docs/integrations/<pkg>/*.md -> libs/integrations/<pkg>/docs/*.md (allegro, dpd-polska, erli, inpost, ksef, prestashop, subiekt, woocommerce) - docs/integrations/woocommerce/screenshots/ -> .../woocommerce/docs/assets/ - docs/assets/erli/ -> libs/integrations/erli/docs/assets/ - docs/assets/subiekt/ -> libs/integrations/subiekt/docs/assets/ (merged with the tutorial screenshots already at .../subiekt/assets/, no filename collisions) - libs/integrations/{ksef,subiekt}/tutorial.md -> .../docs/tutorial.md - libs/integrations/{ksef,subiekt}/assets/ -> .../docs/assets/ Rewrites every relative link and image reference in the moved files (directory depth changed by one level), updates the 8 package READMEs' documentation sections, the root README's integration table, the new-integration issue template, ADR-025's doc links, and the e2e Playwright capture scripts' output paths. Verified all 251 local links in the changed docs resolve. Historical docs/plans/*.md archive entries are intentionally left untouched — they're frozen snapshots per docs/plans/README.md's own convention, not live documentation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014S9kgctitB75BfHEDejJ6B Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(subiekt): drop old personal repo link, switch bridge instructions to plain PowerShell - Remove remaining references to the old norbert-kulus-blockydevs/openlinker-subiekt repo and its without-exe-packaging branch — the bridge now lives directly on openlinker-project/openlinker-subiekt-bridge. - Rewrite "running the bridge" / smoke-test steps as plain Windows PowerShell (Invoke-RestMethod) instead of WSL-flavored bash+curl — the bridge always runs on Windows; WSL was an artifact of how screenshots were captured for this PR, not an operator requirement. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014S9kgctitB75BfHEDejJ6B Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(integrations): declutter, crop, and fix tutorial screenshots Re-captures the KSeF and Subiekt tutorial screenshots against the fully merged dev branch (all 9 in-flight FE PRs) with three fixes: 1. Wizard field-by-field captures consolidated into one "all fields filled" screenshot per wizard (KSeF: 6 shots -> 1; Subiekt: 3 shots -> 1). 2. Noisy/irrelevant data cropped instead of shown in full: - Connections list: crop to header + Add-connection button, hiding unrelated existing connections. - Invoices list: filter by the tutorial's own connection + issued status before capturing, instead of showing the full unfiltered list (which mixed in dozens of unrelated test rows). - Order detail / invoice-issued state: crop above the Sync status/Activity/Order Snapshot sections, which aren't part of the tutorial narrative. - Connection detail page: crop above the "Capabilities" panel, which has an unrelated pre-existing bug (renders literal `’` and "adapter not recognized" for working connections) surfaced by one of the merged branches -- out of scope for this docs PR. 3. Fixed two broken captures: - ksef/docs/assets/14-ol-invoice-detail-ksef.png was an "Invoice not found" error -- GET /invoices/:invoiceId didn't exist on main yet (ships in open PR #1231). Temporarily merged that branch locally to capture the real working page, then reverted the merge. - subiekt order-detail flow (21-25) used a test order with a dangling customerId (no matching customer_projections row) that rendered "Couldn't load customer details. Retry". Backed a real order (ol_order_ksef_e2e_test_001) with a customer_projections row matching its embedded billing address, then drove the actual Issue-invoice flow through the live Subiekt bridge end-to-end. Also drops one redundant intermediate screenshot (24-ol-invoice-issuing-or-issued.png, identical to 25 once the bridge issue was fixed) and removes now-orphaned per-field screenshot files. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014S9kgctitB75BfHEDejJ6B Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(integrations): redact KSeF portal cert/token data, fix platform-picker screenshots - p3/p4 (KSeF portal — NIP + test cert / sign test request): the test certificate's SHA256 fingerprint and ID were shown in plaintext. Redact both (they're live, usable values on the shared public test environment, same category as the token value already redacted in p8). - p6 (KSeF portal token list): was showing the full historical token list (9+ unrelated tokens: "kolejny", "henha", "teest", ...) instead of just the tutorial's own token. Generate a fresh, clearly-named token immediately before capturing so it sorts to the top, crop to header + that one row. - 02-ol-platform-picker.png / 07-ol-platform-picker.png: these were byte-identical between the KSeF and Subiekt tutorials, but their alt text claimed each showed its own platform's card specifically ("KSeF card" / "Subiekt nexo card") — neither image actually highlighted anything. Hover the relevant card before capturing so each tutorial's screenshot genuinely shows what its alt text says. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014S9kgctitB75BfHEDejJ6B Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(ksef): add missing Part 3/4 screenshots, drop unimplemented KOR section Part 3 (get a B2B order in), Part 4 (issue the invoice), and the Subiekt cross-reference in "Next steps" had no corresponding OpenLinker screenshots - three (correction, orders-list, order-detail) were simply never captured. Fills the gap with a real order (ol_order_e2e_tutorial_001, B2B, company buyer) driven through orders-list -> order-detail (not issued) -> connection picker -> ready-to-issue -> issued+accepted, calling the real KSeF test API. Also: - Corrected an inaccurate claim: the tutorial said OpenLinker "pre-selects Invoice (faktura VAT) when a NIP is present" — checked order-invoice-panel.tsx and the document-type select always defaults to 'invoice' with no NIP-aware logic. Removed the false claim. - Removed the entire "Part 6 — Correction invoices (KOR)" section. The Issue-correction dialog is real, working FE (KSeF has an invoiceCorrectionFlow slot), but actually submitting fails with "Provider does not support correction issuance" — KsefInvoicingAdapter doesn't implement the CorrectionIssuer capability. Checked all 11 open PRs; none add it either, so this isn't a merge-order gap, it's an unbuilt backend capability. Replaced with a one-line "coming soon" note in Next steps. - Removed the "Pair with Subiekt nexo" bullet from Next steps per review — the KSeF tutorial should carry zero Subiekt references. - Renamed/consolidated the local (gitignored) capture scripts used to produce these screenshots for future reuse. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014S9kgctitB75BfHEDejJ6B Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(ksef): restore Part 6 (KOR corrections) tutorial with real screenshots CorrectionIssuer shipped on KsefInvoicingAdapter (#1289), so the correction flow that was previously removed from the tutorial (backend didn't support it yet) now works end to end. Captured 4 real screenshots against a live KSeF sandbox correction: the Issue correction button, the filled-in correction dialog, the post-submit confirmation, and the invoices list showing the original + correction rows side by side. Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * chore(docs): address tech-review findings - remove orphaned assets and stray .gitkeep files Delete 34 Subiekt screenshots never referenced by tutorial.md or README.md (an earlier numbering scheme plus a cut PrestaShop-order walkthrough section), and two redundant .gitkeep files - one in a stray top-level libs/integrations/subiekt/assets/ directory confusable with the real docs/assets/, and one in libs/integrations/ksef/docs/assets/ now that it holds 22 real screenshots. Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * test(erli): cover #1146 cancellation stock-restore hook (S7) Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(woocommerce): recapture master-shop screenshots in light mode The 9 WooCommerce master-shop-setup-guide screenshots were dark-mode (captured before this branch existed, pre-dating the docs relocation). Recaptured all of them against a live dev stack in light mode, with the same red-box/arrow callouts drawn programmatically instead of by hand. Adds two reusable Playwright e2e scripts: - apps/web/e2e/annotate.mjs: canvas-overlay helper to draw red rectangle/ellipse annotations (with optional arrow) onto a page before screenshotting, so future tutorial captures don't need a manual image-editor pass. - apps/web/e2e/woocommerce-walkthrough.mjs: drives the WooCommerce connection wizard end-to-end and captures all 9 assets referenced from master-shop-setup-guide.md, forcing light theme via localStorage['openlinker.theme'] regardless of host prefers-color-scheme. Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(integrations): fix Piotr's review findings on PR #1284 - ksef/README.md: fix wrong KSeF API base URLs (was api-test.ksef.mf.gov.pl/api/v2 and ksef.mf.gov.pl/api/v2; authoritative ksef-hosts.ts uses a bare /v2 path with prod host api.ksef.mf.gov.pl). Also documents the demo tier that was missing. - ksef/docs/setup-guide.md: refresh from the stale C2-stub narrative (issuance throws, RegulatoryTransmitter unimplemented, no seller-profile config) to the shipped reality — full FA(3)+KOR issuance, RegulatoryTransmitter and CorrectionIssuer implemented, seller block present on KsefConnectionConfig. - subiekt/docs/tutorial.md: the appsettings.json sample had a fictional "HeaderName": "X-Api-Key" field — the real bridge (AuthOptions) only has Enabled/ApiKey and hardcodes Authorization: Bearer. Removed the field and documented that the client's extra x-bridge-token header is sent but ignored by the bridge. - subiekt/docs/runbook.md: same x-bridge-token clarification. - woocommerce/README.md: capability table was missing CategoryProvisioner (manifest declares 6 capabilities, README listed 5). - allegro/README.md: ADR-024 link pointed at the adrs/ directory instead of the file; repointed to the actual architecture-overview.md#824 section plus the correct ADR-024 file (which covers the related OfferManager/ProductPublisher split, not #824 itself). Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(subiekt): add Windows+WSL2 dev quick-setup guide (Presta + OpenLinker + bridge) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(subiekt): expand PrestaShop run steps + link canonical Getting Started Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(subiekt): use {{USER_NAME}} placeholder instead of a real Windows username Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(subiekt): add optional 'launch bridge from WSL' command box Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(subiekt): document payment-method / bank-account / cash-register per-invoice feature (#1324) Adds setup-guide Part B2 subsection + tutorial Part 2b with live screenshots; FV-only requirement, multi-payer warning, fixed-Centrala note, advanced config keys. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(subiekt): fix broken image links in setup-guide (remap to existing assets, drop unbacked shots) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(ksef): payment config (FA(3) Platnosc) tutorial + KOR snapshot semantics Live-E2E-verified on the KSeF test environment (2026-07-03): - new tutorial Part 2a - per-connection payment config (method, term, bank account, SWIFT, skonto) with fresh screenshots - setup-guide: config.payment field reference + issuance-time line snapshot semantics in the Corrections section (#1297) - README: Platnosc feature bullet - re-captured 12-ol-ksef-detail.png with the post-#1320 capability panel Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(integrations): address Piotr review round 3 on PR #1284 - strip unmerged #1335 content (Subiekt Part 2b + payment/cash-register sections + shots 28-29 + WSL-guide proxy-endpoint step) - rides with PR #1335 instead - fix fabricated credentials/config examples against the real DTOs: KSeF (authType+secret, env+nested seller), DPD (login+password, payerFid string + required senderAddress), WooCommerce (siteUrl, inventory/orders blocks), Allegro (authorization-code flow, environment enum + optional keys), InPost (organizationId string, senderAddress, -pl sandbox host), Erli (real validator keys, ADR-025-correct taxonomy preference, decoder-accurate webhook body), AI (OL_AI_DEFAULT_MODEL / OL_AI_OPENAI_MODEL, gpt-4o-mini) - Subiekt tutorial: manual document-type selection (FE does not NIP-preselect; Part 7 no longer instructs relying on auto-Receipt); README scopes the NIP rule to the auto-issue path - sync KSeF/Subiekt capability tables with the adapters' implements lists; drop the stale Source layout trees - align Subiekt /health response shape with the real bridge response - revert the unrelated Erli S7 int-spec (0c1d07b) - moves to its own PR Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(integrations): post-review consolidation fixes on PR #1284 - merge origin/main so the branch carries the shipped FA(3) Platnosc feature (#1317, b40902e) that the payment docs describe - the docs no longer reference an unmerged feature - repoint 4 inbound links broken by the docs/integrations -> package docs/ move (architecture-overview, getting-started, user-guide 02/04) - Erli README: capability row now matches ErliOfferManagerAdapter's implements list (drop OfferLister/OfferQuantityBatchUpdater, add OfferStatusReader/OfferStockRestorer/TaxonomyBorrower) - align the Part 2a heading dash with sibling headings - woocommerce-walkthrough.mjs: admin password from OL_ADMIN_PASSWORD env instead of a hardcoded shared-instance value Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(plans): repoint dangling links after integrations docs move The docs/integrations -> libs/integrations/<pkg>/docs/ relocation on PR #1284 left two frozen implementation-plan docs pointing at the old paths. Repoint them to the current locations. Addresses the residual N1 finding from the PR #1284 delta re-review. Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(integrations): reconcile README/setup-guide shapes with DTOs; e2e cleanup Address the consolidated /pr-review + /tech-review findings on PR #1284. IMPORTANT: - dpd-polska setup-guide: document payerFid as the required numeric id and masterFid as optional, matching DpdConnectionConfigDto. - ksef setup-guide credentials table: the operator submits { authType, secret } (raw token); secretRef is platform-assigned, kept in prose only. - woocommerce README: OrderProcessorManager supports only OrderFulfillmentUpdater (drop FulfillmentStatusReader / DestinationOptionsReader over-claim). - prestashop README: add ProductPublisher + CategoryProvisioner to prose and capability table (manifest declares 6). Minor: - woocommerce setup-guide: note HTTPS is required (config validator rejects http). - ksef README: clarify RegulatoryStatusReader is covered via RegulatoryTransmitter. - apps/web/e2e: parameterize admin credentials via OL_ADMIN_USERNAME/PASSWORD, mark author-local defaults, drop dead VARIANT const, fix stale localStorage log message, add apps/web/e2e/README.md documenting these as manual capture scripts. - erli setup-guide: wire in 00-dashboard/01-connections-list; drop redundant orphaned 12-order-detail.png. Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> --------- Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs(plans): implementation plan for KSeF FA(3) Platnosc (#1311) Adds the full implementation plan for emitting payment method, bank account, payment term, and skonto in KSeF FA(3) invoices as a per-connection config value (no live bank-accounts API on the KSeF side, unlike inFakt's #1303/#1308). Includes the schema-audited XSD child order for Platnosc (TerminPlatnosci -> FormaPlatnosci -> RachunekBankowy -> Skonto) and the design mockup referenced by the issue. Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * feat(ksef): emit FA(3) Platnosc from connection payment config (#1311) Adds a per-connection, manually-entered payment configuration (default payment method, bank account, payment term, early-payment discount) and emits it into the FA(3) Platnosc element whenever configured. Unlike inFakt (#1303/#1308), KSeF has no live bank-accounts API, so this is a plain config value the operator types in once, mapped straight through the existing seller/defaultTaxRate resolution chain (factory -> adapter -> mapper -> pure builder). - KsefPaymentConfig / KsefBankAccountConfig / KsefFormaPlatnosciValues on KsefConnectionConfig (domain types) - Fa3PaymentInput / Fa3BankAccount / Fa3FormaPlatnosciValues (FA(3) builder-internal types) - platnoscNode() in fa3-xml.builder.ts, emitted as a sibling of FaWiersz in the XSD-mandated child order: TerminPlatnosci -> FormaPlatnosci -> RachunekBankowy -> Skonto (confirmed against the vendored FA(3) v1-0E XSD - not payment-method-first) - KsefAdapterFactory.resolvePayment + shape-validator checks for formaPlatnosci / bankAccount.nrRb / paymentTermDays - FE: ksef-payment-config.ts assembly module (mirrors ksef-seller-config.ts) wired into edit-connection.schema.ts, new fields in ksef-structured-section.tsx - FA3_IMPLEMENTATION_NOTES.md updated with the Platnosc mapping table Backend: 305/305 ksef unit tests pass, including full XSD structural validation of the new Platnosc block for both configured and unconfigured connections. Frontend: 1887/1887 web unit tests pass. check:invariants clean. Closes #1311 Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * fix(ksef): stop dropping payment sub-fields based on fill order (#1311) Live Playwright smoke test against a real dev stack + KSeF sandbox connection (Phase 6 of the implementation plan) surfaced a real bug: applyKsefPaymentToConfig deleted the whole bankAccount/skonto sub-object whenever a sibling field (nrRb, or the other of conditions/amount) wasn't present in the SAME merge call. Since each field syncs to configText independently per keystroke, this silently discarded whatever the operator typed first - skonto could never actually be saved, and a bankAccount sub-field typed before nrRb was lost. Fix: only drop a sub-object when it is completely empty (no keys at all), never based on a missing sibling. The "nrRb required if bankAccount is set" and "skonto needs both conditions+amount" rules now live where they belong - the backend shape validator (save time, new skonto check added) and the factory's resolvePayment (issuance time) - so a violation surfaces as a clear error instead of silent data loss. Adds the e2e smoke-test script (apps/web/e2e/ksef-payment-config.mjs) and its screenshots (docs/assets/ksef-1311-smoke/) used to find this and verify the fix + full field set against the design mockup. Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(plans): mark #1311 acceptance criteria + Phase 6 complete All acceptance criteria satisfied and Phase 6 (live smoke test + verification artifact) done - link to the published artifact and note the persistence bug found/fixed during the smoke test. Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * fix(ksef): address tech-review suggestions on PR #1317 - Shape validator + FE Zod schema now enforce the FA(3) TNrRB length bound (10-34 chars) on payment.bankAccount.nrRb instead of only non-emptiness, so a truncated account number is rejected at connection-save time rather than surfacing as an opaque KSeF XSD error at issuance. - resolvePayment now defensively drops an unknown formaPlatnosci code or a negative/non-integer paymentTermDays (mirroring the existing bankAccount/skonto guards) for connections whose config predates the ksef.publicapi.v2 shape validator. Addresses both SUGGESTION findings from the tech-lead review on #1317. Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * fix(ksef): correct RachunekBankowy child element order (NrRB, SWIFT, NazwaBanku) Confirmed live against the KSeF sandbox: the FA(3) XSD mandates NrRB before SWIFT before NazwaBanku inside RachunekBankowy. Emitting NazwaBanku before SWIFT caused KSeF to reject the invoice (status 450, "invalid child element SWIFT... expected OpisRachunku"). Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * fix(ksef): session IV reuse + FA(3) Podmiot2 required JST/GV fields Two E2E-verified prerequisites for live KSeF acceptance, confirmed against the KSeF test environment while doing live sandbox verification of #1311 (the Platnosc feature was silently blocked from ever actually being accepted end-to-end without these): 1. AES session IV: encryptDocument was generating a fresh random IV per document, but KSeF's SendInvoiceRequest wire shape has no per-document IV field — it decrypts every document in a session using the session IV declared once in OpenOnlineSessionRequest.encryption.initializationVector. A per-document IV caused a SHA-256 hash mismatch (status 430) because KSeF's decryption produced garbage. Fixed: encryptDocument now reuses context.symmetricKey.iv (the session IV) for every document. 2. JST and GV are REQUIRED by the FA(3) XSD on Podmiot2 (no minOccurs="0") but were never emitted, causing a semantic validation rejection. Fixed: buyerNode now always emits JST=2, GV=2 ("nie dotyczy" — not a JST subsidiary unit / not a VAT group member). Cherry-picked from 634c27a5 (previously only on the unmerged branch 1228-ksef-fa3-full-visualization-new, which was never pushed to origin) — without this fix no live KSeF invoice submission from this codebase can be genuinely accepted, regardless of Platnosc content. Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * fix(ksef): address PR #1317 review - Platnosc structural order checks + ctor options bag - validateFa3Xml now enforces the XSD-mandated child order of Fa/Platnosc (TerminPlatnosci -> FormaPlatnosci -> RachunekBankowy -> Skonto) and of each RachunekBankowy (NrRB required, SWIFT before NazwaBanku), so an ordering regression like the pre-review RachunekBankowy bug is caught by the 'passes the structural validator' tests instead of co-signed by them - KsefInvoicingAdapter trailing optional params (payment, now) move into a KsefInvoicingAdapterOptions bag so future additions never shift positional call sites - resolvePayment doc comment now names the intentional three-layer (FE assembly / shape validator / factory) defense-in-depth explicitly The blocking SWIFT/NazwaBanku emit-order finding was already fixed in c315e4d; this adds the validator coverage that would have caught it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * test(web): align stale skonto persistence test with per-keystroke sync fix The merge helper intentionally persists an incomplete skonto (conditions without amount) so independent per-field config sync never drops the first-typed field; completeness is enforced at save/issuance time (#1311 smoke-test finding). The test still asserted the old drop-on-incomplete behaviour. Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * fix(api): version listings invalid-path-id int-spec paths under /v1 (#1328) Semantic merge conflict between #1316 (URI versioning, global /v1 default) and #1313 (new listings-invalid-path-id.int-spec.ts written pre-versioning): the spec's unprefixed /listings/... requests stopped matching any route, so every request returned the router-level 404 ("Cannot GET ...") - the three expect(400) ParseUUIDPipe assertions failed and the expect(404) ones passed vacuously. Main has been red on Integration Tests since the two merged. Prefix the five request paths with /v1, matching every other listings int-spec. Verified against the Testcontainers harness: 5/5 pass. Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * fix(ksef): address second tech-review round on PR #1317 - NrRB input is whitespace-stripped at FE assembly time (new normalizeNrRb, mirroring the normalizeNip precedent) so a conventionally-spaced NRB paste never reaches config.payment.bankAccount.nrRb or the FA(3) wire with inner spaces; the FE zod length check now counts the stripped value, converging with the BE shape validator's 10-34 bound - shape validator rejects a wrong-typed payment / bankAccount / skonto with an explicit 'must be an object' issue instead of silently falling through to issuance-time drop - the tripled '1'..'7' TFormaPlatnosci list now carries a cross-reference comment at all three declaration sites (FE schema, plugin connection-config types, FA3 schema types) so a future 8th code is added everywhere - ksef-payment-config.mjs smoke script reads login credentials from WEB_USER / WEB_PASSWORD env vars (dev-stack defaults preserved), matching the WEB_BASE / KSEF_CONN_ID pattern Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * fix(ksef): address third tech-review round on PR #1317 - shape validator now rejects an nrRb containing inner whitespace (the FE strips via normalizeNrRb, but a direct API write bypassed that and a spaced NRB would fail KSeF's TNrRB pattern at clearance); the factory's resolvePayment additionally strips whitespace defensively for pre-validator config rows, and drops a whitespace-only value instead of emitting an empty NrRB - the hardcoded Podmiot2 JST/GV = 2 limitation is now recorded in FA3_IMPLEMENTATION_NOTES.md under Known limitations, with a pointer comment at the buyerNode declaration site - new drift spec makes the tripled TFormaPlatnosci '1'..'7' list self-enforcing: the two in-package arrays are compared by import and the FE array is extracted from its source file, so a one-sided edit fails the suite - follow-up issue #1330 filed for moving plugin-specific connection-config assembly behind a plugin slot Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * fix(ksef): address fourth tech-review round on PR #1317 - IMPORTANT (skonto error locality): editConnectionSchema gains a both-or-neither superRefine on the skonto pair, anchoring the error on the missing field at submit time instead of surfacing the BE shape validator's form-level 400; per-keystroke persistence of a partial pair stays untouched. Field descriptions now say the pair is required together. - SUGGESTION (drift-guard monorepo coupling): the cross-package FE half of the TFormaPlatnosci drift guard moved out of the plugin jest suite (7-level relative path into apps/web) into a repo-level invariant, scripts/check-ksef-forma-platnosci-drift.mjs, wired into check:invariants; the in-package spec keeps the import-level comparison. - SUGGESTION (paymentTermDays unbounded): sanity cap at 999 days on both sides (FE zod refine + BE shape validator 0-999), with tests at and above the bound. - SUGGESTION (misleading mockup filename): docs/plans/mockups/ infakt-ksef-bank-account-payment-terms.html renamed to ksef-payment-platnosc.html; all references updated. - SUGGESTION (PR scope / merge-commit visibility): handled in the PR body (dedicated line for the session-IV crypto fix so it survives into the squash-merge message). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(ksef): cite spec sections confirming single-IV-per-session AES model Review nit on PR #1317 (approve round): the session-IV reuse in encryptDocument was justified only via the CIRFMF C# reference client. The doc comments in ksef-session-crypto.service.ts and ksef-crypto.types.ts now also cite the KSeF 2.0 OpenAPI (OpenOnlineSessionRequest.encryption.initializationVector is the only IV on the wire; SendInvoiceRequest has no per-document IV field) and the official CIRFMF session guide (sesja-interaktywna.md: one 256-bit key + 128-bit IV generated at session open, used for the session's documents). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * refactor(web): move plugin-specific connection-config assembly behind a plugin slot (#1334) * refactor(web): move plugin-specific connection-config assembly behind a plugin slot Introduce PlatformContribution.connectionConfig (#1330) - the non-render half of a platform's structured connection-config editing: a Zod schema fragment composed into the edit-connection resolver at render time, a read-side readConfigToForm hydrator, and a write-side applyToConfig partial-patch assembler. Field names are declaration-merged into the new PluginEditConnectionFields interface (mirrors the PluginApiNamespaces precedent) so plugin sections stay statically typed. Migrate KSeF as the first platform: every ksef-named module leaves the shared features/connections feature (seller/payment assembly, NIP/NRB normalizers, setup wizard schema+form+page, value sets) and lands in the plugins/ksef slice; the KSeF Zod fields, superRefine checks, merge clauses, and hydration readers move verbatim into plugins/ksef/ksef-connection-config.ts, preserving the #1311 per-keystroke partial-patch semantics (relocated tests pin them). Other platforms' inline fields migrate as follow-ups now that the seam exists. Closes #1330 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * refactor(web): tighten the connection-config plugin-slot contract per tech review Address the tech-review round on PR #1334 (1 IMPORTANT + 2 SUGGESTIONS): - IMPORTANT: constrain ConnectionConfigContribution.schemaShape to a keyof-PluginEditConnectionFields mapped type (PluginConnectionConfigSchemaShape) so a schema key with no matching declaration-merged form field is a compile error. The synthetic acmeToken test fixture now declaration-merges its field (same seam real plugins use), which also drops the patch-type casts it needed. - SUGGESTION: extend assertUniquePluginInvariants with invariant 4 - cross-plugin connectionConfig.schemaShape field-name collisions throw at module load (TS silently accepts same-type declaration merges), with a registry test pinning the failure path. - SUGGESTION: dev-mode console.warn in syncStructuredToJson when a synced field has neither a host merge clause nor a connectionConfig contribution (the silent-drop misconfiguration #1330 exists to prevent); StructuredField is now derived from a runtime as-const array. Also drop the redundant defensive spread before applyToConfig. Doc row for connectionConfig updated with the compiler constraint, the boot-time collision guard, and the platform-prefix naming convention. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * refactor(web): annotate ksefSchemaShape + widen the schemaShape dev-warn per second-pass review Second-pass tech-review SUGGESTION: the excess-property check from the PluginConnectionConfigSchemaShape mapped type only fires on a fresh or annotated literal, so the separate un-annotated ksefSchemaShape const silently accepted unmerged keys. Annotating it with ConnectionConfigContribution['schemaShape'] restores the check at the declaration (verified: a bogus key now fails with TS2353). Also closes the related note: syncStructuredToJson's dev warn now fires not only when a platform ships no connectionConfig contribution, but also when the contribution exists and the synced field is absent from its schemaShape - the other runtime-detectable silent-drop shape. Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> --------- Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * fix(ksef): address fifth tech-review round on PR #1317 - Record the AES-256-CBC session-IV determinism trade-off under Known limitations in FA3_IMPLEMENTATION_NOTES.md, next to the JST/GV entry, so security reviewers find it where they look first. - Harden the TFormaPlatnosci drift script's extraction: tolerate an optional type annotation, Prettier multi-line wraps, and inline comments inside the array body. - Promote the untyped-bag string readers (readConfigString / readOptionalConfigString) to shared/plugins; ksef-connection-config and EditConnectionForm now share one definition instead of per-file copies. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * fix(ci): reclaim disk from orphaned Testcontainers volumes/images/build-cache Ryuk is disabled on the persistent self-hosted runner, so the manual orphan-sweep step is the only cleanup for Testcontainers resources. docker rm -f (no -v) left anonymous volumes behind forever, and the sweep never touched dangling images or build cache — both accumulate indefinitely and were the actual cause of "no space left on device" integration-test failures unrelated to any test's own code. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * fix(ci): widen disk cleanup after first attempt reclaimed 0B (#1321) The prior sweep (docker rm -fv + filtered volume/image/builder prune) still failed with the exact same "no space left on device" error, and logged 0B reclaimed across all three prune calls. Two concrete bugs: - The volume-prune label filter never matches: anonymous volumes don't inherit the labels of the container that created them, so `--filter "label=org.testcontainers=true"` matches nothing. Dropped the filter — an unfiltered `docker volume prune` is still safe, since Docker refuses to remove a volume still attached to any container regardless of label. - Plain `docker image prune -f` only removes dangling (untagged) images; 0B reclaimed means this runner has none, so the real disk pressure is old-but-tagged image versions across many past integration-test pulls. Widened to `-af` (all unused images) — still safe under concurrency since Docker never removes an image a live container references, and a concurrent job needing a pruned image just re-pulls it rather than failing. Also logs `docker system df -v` before/after cleanup so the next failure (if any) can be diagnosed from CI output alone — there is no shell access to this runner. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * revert(ci): back out disk-cleanup experiment (#1321) docker system df on the failing run revealed this self-hosted runner is shared with unrelated third-party workloads (linux-runner-1..4, hedera-payment-db, hawser-dockhand-agent — none of it OpenLinker's). The runner's disk pressure is caused by that other tenancy's live containers, not by anything OpenLinker's CI leaves behind, so a wider prune (image -af, unfiltered volume prune, builder prune) only ever reclaims OpenLinker's own garbage and can't fix a permanently oversubscribed shared host — while carrying real risk of touching image/volume state a co-tenant relies on. Reverting to the original, narrowly-scoped cleanup: remove only OpenLinker's own orphaned, Testcontainers-labeled containers whose owning CI run has finished. Disk capacity itself needs to be resolved with whoever operates this runner, not from this repo's workflow. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * diag(test): log docker disk usage around each PS+MySQL container boot (#1321) A full apps/api integration run boots 4 suite-scoped PrestaShop+MySQL Testcontainer pairs sequentially (maxWorkers: 1), and Ryuk is disabled on the self-hosted CI runner — so a suite that times out before reaching its own cleanup() leaks disk for the rest of that run with no way to tell, from CI output alone, which suite did it (a prior "no space left on device" mid-run failure had exactly this shape). Logs `docker system df` before each boot and after each cleanup() in the shared prestashop-container.helper.ts, so the next such failure can be attributed to a specific suite without runner shell access. Considered also converting the two PS suites that don't install the OL module (prestashop-harness-smoke, prestashop-webhook-provisioning) to mock the port instead of booting a real container, to cut boot count from 4 to 2. Both are deliberately real-PS regression guards — the smoke test exists to prove the Testcontainer harness itself works end-to-end, and the webhook-provisioning spec guards #541, a bug that was green at the unit-test/mock layer and only caught against the real WS API. Mocking either would remove the exact coverage they exist for, so this is diagnostics-only; reducing real PS boot count needs a different approach (e.g. sharing one boot between the two suites that already require installOlModule: true). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * diag(ci): probe host /tmp for leaked ol-ps-data-* PrestaShop bind-mount dirs (#1321) The PS test harness bind-mounts a mkdtemp dir onto /var/www/html and PS writes ~1GB into it as root; the unprivileged rmSync cleanup leaks it on the persistent self-hosted host, invisible to docker system df and the container sweep. Temporary read-only probe to confirm before fixing the cleanup path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * fix(test): stop leaking root-owned PS bind-mount dirs on the CI host (#1321) PS writes ~1GB into the ol-ps-data-* bind-mount as root, and on the runner-in-docker host the source path resolves on the HOST daemon's /tmp - the unprivileged rmSync cleanup could never delete it, so every PS suite leaked ~1GB onto the host root fs until containerd hit "no space left on device". Two-part fix: - removePsDataDir empties the dir through a root alpine container (works in both host topologies), then rmSync's the local dir. - The CI orphan-sweep step reaps host /tmp/ol-ps-data-* dirs older than 90 min (same age gate as the container sweep), covering SIGKILLed/cancelled runs that never reach in-process cleanup. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * revert(ci): drop temporary disk diagnostics + host-tmp sweep steps (#1321) Restores ci.yml to main's version. With the host root fs at 100% every docker-cli invocation in these steps hangs or fails, blocking the job before tests even start. The in-process removePsDataDir fix in the PS container helper stays - it adds no CI step. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * Revert "diag(test): log docker disk usage around each PS+MySQL container boot (#1321)" This reverts commit eea68ac. Diagnostics no longer needed - the root cause (leaked root-owned ol-ps-data-* bind-mount dirs on the host) is identified and fixed by removePsDataDir. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> --------- Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…rrections (#1297) (#1329) * docs(invoicing): implementation plan for issuance-time line snapshot (#1297) Plan for persisting a neutral issuance-time line snapshot on InvoiceRecord so KSeF (and any complete-resubmit) corrections diff against the lines as issued, not the order's current state. Branch cut from main (not #1317 - zero file overlap, snapshot is pure-core in InvoiceService). Refs #1297 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * feat(invoicing): persist issuance-time line snapshot for safe corrections (#1297) Persist a neutral issuedLineSnapshot ({ buyer, currency, lines }) on InvoiceRecord whenever a document is issued (from the issue command) or corrected (the correction's own post-correction lines), captured in the core InvoiceService - no adapter change. The correction endpoint now assembles originalDocument from that snapshot on the document being corrected, so a KOR's originalLineNumber-indexed deltas diff against the lines AS ISSUED (and, for a correction-of-correction, against the prior correction's own lines) even if the order changed since issuance. Records issued before the column existed fall back to the pre-#1297 order-derived reconstruction. - New IssuedLineSnapshot type + nullable jsonb column + migration 1818000000003. - InvoiceService.issueInvoice/issueCorrection populate it via InvoiceOutcomePatch; applyCorrectionDeltas computes the correction's after-lines from the original snapshot + per-line deltas. - Controller prefers the persisted snapshot (buildSnapshotFromRecord), skipping the order fetch; falls back to order-derived only when absent. - Not exposed via any response DTO (carries buyer PII), mirroring documentContent/sourceDocument. Tests: service snapshot on issue + 4 correction cases; controller snapshot- preference + correction-of-correction; repository jsonb round-trip int-spec. Closes #1297 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * refactor(invoicing): address tech-review suggestions on line snapshot (#1297) - Reject duplicate originalLineNumber correction lines at the HTTP boundary (new UniqueOriginalLineNumbersConstraint + DTO spec) so the persisted "after" snapshot can never silently last-write-win against what the provider computed; applyCorrectionDeltas doc notes the residual duplicate semantics for non-API callers. - Type IssuedLineSnapshot.buyer as the structural IssuedSnapshotBuyer shape instead of the BuyerProfile class - the field round-trips through jsonb without the class prototype, so the type now says so. - Document the transitional taxId: null caveat on buildSnapshotFromRecord for snapshots seeded by a correction of a pre-#1297 record (one-hop degradation that self-heals). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * refactor(invoicing): non-null-assert guarded fields in correction snapshot Per PR review: buildSnapshotFromRecord's `?? ''` / ternary `''` fallbacks for documentNumber/issueDate were dead branches masking the caller's existing non-null guard (providerInvoiceNumber/issuedAt asserted before this path runs). A future guard regression would now surface as a type error / crash instead of silently producing an invalid empty string. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> --------- Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Repairs the BLOCKING merge-state defect and all IMPORTANT/SUGGESTION findings from the /pr-review on #1335. BLOCKING — the branch had merged an older main and, via a bad conflict resolution, silently reverted three shipped features (#1297 issued-line snapshot, #1317 KSeF FA(3) Płatność, #1330 connection-config plugin slot, plus release-please plumbing). Reset the branch to origin/main and re-applied only the Subiekt-scoped delta on top, so the diff is now the Subiekt-only change and no merged work is reverted. IMPORTANT 1. Payment/bank/cash-register config now rides the #1330 ConnectionConfigContribution plugin slot (new plugins/subiekt/subiekt-connection-config.ts) instead of growing the host edit-connection.schema.ts. 2. Payment method is a real tri-state: an explicit "Not set (Subiekt default)" option, and the summary derives from the actual state (no false "Cash" for unset). 3. use-set-default-bank-account-mutation now invalidates the owner-aware subiektBankAccounts key too, so isDefault flags don't go stale. 4. The section fires the default-sync via .mutate() (not void mutateAsync) to avoid an unhandled promise rejection on failure. 5. The set-default error toast copy is now provider-neutral (was inFakt). 6. Removed the stale "does not type-check (3-arg constructor)" comment in subiekt-adapter.factory.ts — the 4-arg constructor ships here. SUGGESTIONS - paymentFields() warns when transfer is configured without a bankAccountId. - setDefaultBankAccount guards the Number(accountId) coercion, throwing the config domain error on a non-numeric id. - Payment labels routed through t(); the cash-register help default is English. - Bank accounts group by the stable ownerPodmiotId, not the display name. Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… Subiekt nexo (#1284) * docs(integrations): add README + operator tutorials for all 8 adapter packages Add per-package README.md to every integration package that was missing one: ai, allegro, dpd-polska, erli, inpost, ksef, subiekt, woocommerce. Each README covers adapter key + capabilities, credentials/config shape, and links to further operator docs. Add full A-to-Z operator tutorials for KSeF and Subiekt nexo with per-step screenshot placeholders (ksef/tutorial.md, subiekt/tutorial.md). The Subiekt tutorial covers bridge setup via PowerShell/WSL (without-exe-packaging branch), wizard, B2B faktura, B2C paragon, and idempotency. The KSeF tutorial covers token generation on the MF portal, connection wizard, B2B order, issuance, and UPO download. Create libs/integrations/ksef/assets/ and libs/integrations/subiekt/assets/ directories for future screenshot captures. Update root README Integrations table to link the tutorial.md files for KSeF and Subiekt nexo. Closes #1265 Closes #1266 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014NQB4zBWSrneR71TRKkx1t Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(integrations): add real Playwright screenshots + rewrite KSeF and Subiekt tutorials Replaces all placeholder screenshot references in KSeF and Subiekt nexo tutorials with actual Playwright captures taken against a live OpenLinker instance (preview build with all in-flight FE PRs merged). KSeF assets (14 PNGs): 01-02: connections list + platform picker 03-09: wizard fields (name, env, NIP, address, auth-type, secret) 10-12: connection created, list, detail page 13-14: invoices list with regulatory badges, invoice detail Subiekt assets (18 PNGs): 06-15: connection wizard (empty -> filled -> created -> test -> list -> detail) 20-26: invoice flow (orders list, order detail, connection picker, ready-to-issue, issue clicked, issued state, invoices list) Tutorial rewrites: - ksef/tutorial.md: reordered around actual wizard fields; Part 1 (KSeF portal) marked manual; Parts 2-5 use real screenshot refs - subiekt/tutorial.md: full rewrite to match connection-picker flow (multiple Invoicing connections), real bridge startup instructions, TLS note, idempotency Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014S9kgctitB75BfHEDejJ6B Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(subiekt): add real Subiekt nexo FS document screenshot to tutorial Part 5 (verify in Subiekt nexo) was the only remaining manual-step placeholder without a real capture. Replaces it with an actual screenshot of an issued FS document open in Subiekt nexo desktop. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014S9kgctitB75BfHEDejJ6B Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(ksef): add real KSeF 2.0 test-portal screenshots for token generation Part 1 (get a KSeF authorisation token) was the last manual-step placeholder. Captures the full flow on the live ap-test.ksef.mf.gov.pl portal: test-auth login, NIP + test certificate, dashboard, token list, generate-token form, and the revealed token (value redacted before commit since it's a live, usable test-environment credential). Also corrects the test-portal URL: ksef-test.mf.gov.pl (KSeF 1.0) was decommissioned 2025-09-01; the current test environment is ap-test.ksef.mf.gov.pl (KSeF 2.0). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014S9kgctitB75BfHEDejJ6B Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(subiekt): point to the openlinker-subiekt-bridge repo The bridge is being moved to its own repo under the openlinker-project org (not yet published). Update README + tutorial repo links and clone paths accordingly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014S9kgctitB75BfHEDejJ6B Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(integrations): move all integration docs + assets under libs/integrations/<pkg>/docs/ Establishes one consistent convention across all 8 integration packages: docs (setup guides, runbooks, tutorials) and their image assets live at libs/integrations/<pkg>/docs/ and libs/integrations/<pkg>/docs/assets/, co-located with the code they document instead of split across a separate docs/integrations/<pkg>/ tree. Moves (via git mv, history preserved): - docs/integrations/<pkg>/*.md -> libs/integrations/<pkg>/docs/*.md (allegro, dpd-polska, erli, inpost, ksef, prestashop, subiekt, woocommerce) - docs/integrations/woocommerce/screenshots/ -> .../woocommerce/docs/assets/ - docs/assets/erli/ -> libs/integrations/erli/docs/assets/ - docs/assets/subiekt/ -> libs/integrations/subiekt/docs/assets/ (merged with the tutorial screenshots already at .../subiekt/assets/, no filename collisions) - libs/integrations/{ksef,subiekt}/tutorial.md -> .../docs/tutorial.md - libs/integrations/{ksef,subiekt}/assets/ -> .../docs/assets/ Rewrites every relative link and image reference in the moved files (directory depth changed by one level), updates the 8 package READMEs' documentation sections, the root README's integration table, the new-integration issue template, ADR-025's doc links, and the e2e Playwright capture scripts' output paths. Verified all 251 local links in the changed docs resolve. Historical docs/plans/*.md archive entries are intentionally left untouched — they're frozen snapshots per docs/plans/README.md's own convention, not live documentation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014S9kgctitB75BfHEDejJ6B Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(subiekt): drop old personal repo link, switch bridge instructions to plain PowerShell - Remove remaining references to the old norbert-kulus-blockydevs/openlinker-subiekt repo and its without-exe-packaging branch — the bridge now lives directly on openlinker-project/openlinker-subiekt-bridge. - Rewrite "running the bridge" / smoke-test steps as plain Windows PowerShell (Invoke-RestMethod) instead of WSL-flavored bash+curl — the bridge always runs on Windows; WSL was an artifact of how screenshots were captured for this PR, not an operator requirement. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014S9kgctitB75BfHEDejJ6B Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(integrations): declutter, crop, and fix tutorial screenshots Re-captures the KSeF and Subiekt tutorial screenshots against the fully merged dev branch (all 9 in-flight FE PRs) with three fixes: 1. Wizard field-by-field captures consolidated into one "all fields filled" screenshot per wizard (KSeF: 6 shots -> 1; Subiekt: 3 shots -> 1). 2. Noisy/irrelevant data cropped instead of shown in full: - Connections list: crop to header + Add-connection button, hiding unrelated existing connections. - Invoices list: filter by the tutorial's own connection + issued status before capturing, instead of showing the full unfiltered list (which mixed in dozens of unrelated test rows). - Order detail / invoice-issued state: crop above the Sync status/Activity/Order Snapshot sections, which aren't part of the tutorial narrative. - Connection detail page: crop above the "Capabilities" panel, which has an unrelated pre-existing bug (renders literal `’` and "adapter not recognized" for working connections) surfaced by one of the merged branches -- out of scope for this docs PR. 3. Fixed two broken captures: - ksef/docs/assets/14-ol-invoice-detail-ksef.png was an "Invoice not found" error -- GET /invoices/:invoiceId didn't exist on main yet (ships in open PR #1231). Temporarily merged that branch locally to capture the real working page, then reverted the merge. - subiekt order-detail flow (21-25) used a test order with a dangling customerId (no matching customer_projections row) that rendered "Couldn't load customer details. Retry". Backed a real order (ol_order_ksef_e2e_test_001) with a customer_projections row matching its embedded billing address, then drove the actual Issue-invoice flow through the live Subiekt bridge end-to-end. Also drops one redundant intermediate screenshot (24-ol-invoice-issuing-or-issued.png, identical to 25 once the bridge issue was fixed) and removes now-orphaned per-field screenshot files. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014S9kgctitB75BfHEDejJ6B Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(integrations): redact KSeF portal cert/token data, fix platform-picker screenshots - p3/p4 (KSeF portal — NIP + test cert / sign test request): the test certificate's SHA256 fingerprint and ID were shown in plaintext. Redact both (they're live, usable values on the shared public test environment, same category as the token value already redacted in p8). - p6 (KSeF portal token list): was showing the full historical token list (9+ unrelated tokens: "kolejny", "henha", "teest", ...) instead of just the tutorial's own token. Generate a fresh, clearly-named token immediately before capturing so it sorts to the top, crop to header + that one row. - 02-ol-platform-picker.png / 07-ol-platform-picker.png: these were byte-identical between the KSeF and Subiekt tutorials, but their alt text claimed each showed its own platform's card specifically ("KSeF card" / "Subiekt nexo card") — neither image actually highlighted anything. Hover the relevant card before capturing so each tutorial's screenshot genuinely shows what its alt text says. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014S9kgctitB75BfHEDejJ6B Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(ksef): add missing Part 3/4 screenshots, drop unimplemented KOR section Part 3 (get a B2B order in), Part 4 (issue the invoice), and the Subiekt cross-reference in "Next steps" had no corresponding OpenLinker screenshots - three (correction, orders-list, order-detail) were simply never captured. Fills the gap with a real order (ol_order_e2e_tutorial_001, B2B, company buyer) driven through orders-list -> order-detail (not issued) -> connection picker -> ready-to-issue -> issued+accepted, calling the real KSeF test API. Also: - Corrected an inaccurate claim: the tutorial said OpenLinker "pre-selects Invoice (faktura VAT) when a NIP is present" — checked order-invoice-panel.tsx and the document-type select always defaults to 'invoice' with no NIP-aware logic. Removed the false claim. - Removed the entire "Part 6 — Correction invoices (KOR)" section. The Issue-correction dialog is real, working FE (KSeF has an invoiceCorrectionFlow slot), but actually submitting fails with "Provider does not support correction issuance" — KsefInvoicingAdapter doesn't implement the CorrectionIssuer capability. Checked all 11 open PRs; none add it either, so this isn't a merge-order gap, it's an unbuilt backend capability. Replaced with a one-line "coming soon" note in Next steps. - Removed the "Pair with Subiekt nexo" bullet from Next steps per review — the KSeF tutorial should carry zero Subiekt references. - Renamed/consolidated the local (gitignored) capture scripts used to produce these screenshots for future reuse. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014S9kgctitB75BfHEDejJ6B Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(ksef): restore Part 6 (KOR corrections) tutorial with real screenshots CorrectionIssuer shipped on KsefInvoicingAdapter (#1289), so the correction flow that was previously removed from the tutorial (backend didn't support it yet) now works end to end. Captured 4 real screenshots against a live KSeF sandbox correction: the Issue correction button, the filled-in correction dialog, the post-submit confirmation, and the invoices list showing the original + correction rows side by side. Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * chore(docs): address tech-review findings - remove orphaned assets and stray .gitkeep files Delete 34 Subiekt screenshots never referenced by tutorial.md or README.md (an earlier numbering scheme plus a cut PrestaShop-order walkthrough section), and two redundant .gitkeep files - one in a stray top-level libs/integrations/subiekt/assets/ directory confusable with the real docs/assets/, and one in libs/integrations/ksef/docs/assets/ now that it holds 22 real screenshots. Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * test(erli): cover #1146 cancellation stock-restore hook (S7) Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(woocommerce): recapture master-shop screenshots in light mode The 9 WooCommerce master-shop-setup-guide screenshots were dark-mode (captured before this branch existed, pre-dating the docs relocation). Recaptured all of them against a live dev stack in light mode, with the same red-box/arrow callouts drawn programmatically instead of by hand. Adds two reusable Playwright e2e scripts: - apps/web/e2e/annotate.mjs: canvas-overlay helper to draw red rectangle/ellipse annotations (with optional arrow) onto a page before screenshotting, so future tutorial captures don't need a manual image-editor pass. - apps/web/e2e/woocommerce-walkthrough.mjs: drives the WooCommerce connection wizard end-to-end and captures all 9 assets referenced from master-shop-setup-guide.md, forcing light theme via localStorage['openlinker.theme'] regardless of host prefers-color-scheme. Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(integrations): fix Piotr's review findings on PR #1284 - ksef/README.md: fix wrong KSeF API base URLs (was api-test.ksef.mf.gov.pl/api/v2 and ksef.mf.gov.pl/api/v2; authoritative ksef-hosts.ts uses a bare /v2 path with prod host api.ksef.mf.gov.pl). Also documents the demo tier that was missing. - ksef/docs/setup-guide.md: refresh from the stale C2-stub narrative (issuance throws, RegulatoryTransmitter unimplemented, no seller-profile config) to the shipped reality — full FA(3)+KOR issuance, RegulatoryTransmitter and CorrectionIssuer implemented, seller block present on KsefConnectionConfig. - subiekt/docs/tutorial.md: the appsettings.json sample had a fictional "HeaderName": "X-Api-Key" field — the real bridge (AuthOptions) only has Enabled/ApiKey and hardcodes Authorization: Bearer. Removed the field and documented that the client's extra x-bridge-token header is sent but ignored by the bridge. - subiekt/docs/runbook.md: same x-bridge-token clarification. - woocommerce/README.md: capability table was missing CategoryProvisioner (manifest declares 6 capabilities, README listed 5). - allegro/README.md: ADR-024 link pointed at the adrs/ directory instead of the file; repointed to the actual architecture-overview.md#824 section plus the correct ADR-024 file (which covers the related OfferManager/ProductPublisher split, not #824 itself). Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(subiekt): add Windows+WSL2 dev quick-setup guide (Presta + OpenLinker + bridge) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(subiekt): expand PrestaShop run steps + link canonical Getting Started Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(subiekt): use {{USER_NAME}} placeholder instead of a real Windows username Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(subiekt): add optional 'launch bridge from WSL' command box Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(subiekt): document payment-method / bank-account / cash-register per-invoice feature (#1324) Adds setup-guide Part B2 subsection + tutorial Part 2b with live screenshots; FV-only requirement, multi-payer warning, fixed-Centrala note, advanced config keys. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(subiekt): fix broken image links in setup-guide (remap to existing assets, drop unbacked shots) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(ksef): payment config (FA(3) Platnosc) tutorial + KOR snapshot semantics Live-E2E-verified on the KSeF test environment (2026-07-03): - new tutorial Part 2a - per-connection payment config (method, term, bank account, SWIFT, skonto) with fresh screenshots - setup-guide: config.payment field reference + issuance-time line snapshot semantics in the Corrections section (#1297) - README: Platnosc feature bullet - re-captured 12-ol-ksef-detail.png with the post-#1320 capability panel Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(integrations): address Piotr review round 3 on PR #1284 - strip unmerged #1335 content (Subiekt Part 2b + payment/cash-register sections + shots 28-29 + WSL-guide proxy-endpoint step) - rides with PR #1335 instead - fix fabricated credentials/config examples against the real DTOs: KSeF (authType+secret, env+nested seller), DPD (login+password, payerFid string + required senderAddress), WooCommerce (siteUrl, inventory/orders blocks), Allegro (authorization-code flow, environment enum + optional keys), InPost (organizationId string, senderAddress, -pl sandbox host), Erli (real validator keys, ADR-025-correct taxonomy preference, decoder-accurate webhook body), AI (OL_AI_DEFAULT_MODEL / OL_AI_OPENAI_MODEL, gpt-4o-mini) - Subiekt tutorial: manual document-type selection (FE does not NIP-preselect; Part 7 no longer instructs relying on auto-Receipt); README scopes the NIP rule to the auto-issue path - sync KSeF/Subiekt capability tables with the adapters' implements lists; drop the stale Source layout trees - align Subiekt /health response shape with the real bridge response - revert the unrelated Erli S7 int-spec (0c1d07b) - moves to its own PR Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(integrations): post-review consolidation fixes on PR #1284 - merge origin/main so the branch carries the shipped FA(3) Platnosc feature (#1317, b40902e) that the payment docs describe - the docs no longer reference an unmerged feature - repoint 4 inbound links broken by the docs/integrations -> package docs/ move (architecture-overview, getting-started, user-guide 02/04) - Erli README: capability row now matches ErliOfferManagerAdapter's implements list (drop OfferLister/OfferQuantityBatchUpdater, add OfferStatusReader/OfferStockRestorer/TaxonomyBorrower) - align the Part 2a heading dash with sibling headings - woocommerce-walkthrough.mjs: admin password from OL_ADMIN_PASSWORD env instead of a hardcoded shared-instance value Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(plans): repoint dangling links after integrations docs move The docs/integrations -> libs/integrations/<pkg>/docs/ relocation on PR #1284 left two frozen implementation-plan docs pointing at the old paths. Repoint them to the current locations. Addresses the residual N1 finding from the PR #1284 delta re-review. Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> * docs(integrations): reconcile README/setup-guide shapes with DTOs; e2e cleanup Address the consolidated /pr-review + /tech-review findings on PR #1284. IMPORTANT: - dpd-polska setup-guide: document payerFid as the required numeric id and masterFid as optional, matching DpdConnectionConfigDto. - ksef setup-guide credentials table: the operator submits { authType, secret } (raw token); secretRef is platform-assigned, kept in prose only. - woocommerce README: OrderProcessorManager supports only OrderFulfillmentUpdater (drop FulfillmentStatusReader / DestinationOptionsReader over-claim). - prestashop README: add ProductPublisher + CategoryProvisioner to prose and capability table (manifest declares 6). Minor: - woocommerce setup-guide: note HTTPS is required (config validator rejects http). - ksef README: clarify RegulatoryStatusReader is covered via RegulatoryTransmitter. - apps/web/e2e: parameterize admin credentials via OL_ADMIN_USERNAME/PASSWORD, mark author-local defaults, drop dead VARIANT const, fix stale localStorage log message, add apps/web/e2e/README.md documenting these as manual capture scripts. - erli setup-guide: wire in 00-dashboard/01-connections-list; drop redundant orphaned 12-order-detail.png. Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> --------- Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
Platnoscelement (payment method, bank account, payment term, skonto) as a per-connection, manually-entered config value in the KSeF integration.docs/plans/implementation-plan-ksef-payment-platnosc.md).resolveSeller/resolveDefaultTaxRatefactory -> adapter -> mapper -> pure-builder chain.Platnosc:TerminPlatnosci->FormaPlatnosci->RachunekBankowy->Skonto(NOT payment-method-first, despite that being the more intuitive reading order) -Platnoscitself is a sibling ofFaWierszunderFa, not nested inside it.libs/integrations/ksefand its FE plugin section (ADR-026 compliant).git logline: AES session-IV reuse fix in the KSeF session crypto (protocol-mandated by the KSeF v2 wire shape; fresh per-document IVs failed with status 430; documented CBC-determinism trade-off) - details under "Also included" below.What's included
KsefPaymentConfig/KsefBankAccountConfig/KsefFormaPlatnosciValuesonKsefConnectionConfig;Fa3PaymentInput/Fa3BankAccount/Fa3FormaPlatnosciValuesfor the builder.platnoscNode()infa3-xml.builder.ts, wired intofaNode()right afterFaWiersz; omitsPlatnoscentirely when nothing is configured (existing connections keep byte-identical output).KsefAdapterFactory.resolvePayment(mirrorsresolveSeller/resolveDefaultTaxRate) threaded through the adapter constructor andFa3MappingContext.KsefConnectionConfigShapeValidatorAdapterrejects an unknownformaPlatnosci, an emptybankAccount.nrRb, a negative/non-integerpaymentTermDays, or an incompleteskonto(added after the smoke-test bug below) at connection-save time.ksef-payment-config.tsassembly module (mirrorsksef-seller-config.ts) wired intoedit-connection.schema.ts+EditConnectionForm.tsx; new fields inksef-structured-section.tsx(payment method select + bank account/SWIFT/term/skonto inputs, each crediting its FA(3) target element in the description copy).FA3_IMPLEMENTATION_NOTES.mdupdated with thePlatnoscmapping table and emit order.docs/plans/mockups/ksef-payment-platnosc.html(added in the plan commit; renamed frominfakt-ksef-bank-account-payment-terms.htmlper review - the mockup is the KSeF payment section, the old prefix only credited the inFakt precedent).Bug found via the live smoke test (fixed in this PR)
ksef-payment-config.ts'sapplyKsefPaymentToConfigoriginally deleted the wholebankAccount/skontosub-object whenever a sibling field wasn't present in the same merge call. Since each field syncs toconfigTextindependently per keystroke, this silently discarded whatever the operator typed first —skontocould never actually be saved. Fixed: a sub-object is now dropped only when it's completely empty, never based on a missing sibling; the "required together" rules moved to the backend shape validator (save time) and the factory'sresolvePayment(issuance time). Full writeup + before/after screenshots in the verification artifact.Test plan
pnpm --filter @openlinker/integrations-ksef test- 305+ pass, including full XSD structural validation of the newPlatnoscblock for both configured and unconfigured connections, in the correct child order.pnpm --filter @openlinker/webvitest - full suite pass, including newksef-payment-config/ksef-structured-sectioncoverage.pnpm check:invariants- clean (cross-context imports, service interfaces, etc.).pnpm --filter @openlinker/integrations-ksef lint/pnpm --filter @openlinker/web lint- no new errors or warnings in touched files.Closes #1311
Closes #1330 (assembly-relocation refactor merged into this branch via PR #1334; it targets the default branch through this PR)
Also included (bundled changes surfaced by tech review)
Two live-E2E bug fixes and one drive-by CI fix ride along on this branch; they are documented here so the trade-offs are auditable from the PR, not only from the diff:
ksef-session-crypto.service.ts,ksef-crypto.types.ts):encryptDocumentnow reuses the session-declared IV instead of generating a fresh per-document IV. The KSeF v2 wire shape has no per-document IV field and the CIRFMF reference client confirms session-IV reuse is the protocol's intended model - a fresh IV made every submission fail with status 430. Trade-off: reusing one IV across documents within a session yields deterministic ciphertext for identical plaintexts in that session (a known CBC property). Accepted because the protocol mandates it, the session key is short-lived, and invoice payloads are practically never byte-identical (timestamps, invoice numbers).JST/GVelements inPodmiot2(fa3-xml.builder.tsbuyerNode): the FA(3) XSD declares these withoutminOccurs="0", so omitting them failed live clearance with status 450. Emitted with the2("no") values for regular buyers./v1/listingsint-spec fix (fix(api): version listings invalid-path-id int-spec paths under /v1 #1328, merged into this branch): main's Integration Tests job was red due to a semantic merge conflict between feat(api): version the HTTP API under /v1 + runtime version surface (#1133) #1316 (global/v1URI versioning) and fix(listings): reject non-UUID path ids with 400 instead of 500 #1313 (a new int-spec calling unprefixed/listings/...); the spec's paths are now versioned. Unrelated toPlatnoscbut required for this PR's CI to be green.