Skip to content

docs(infakt): ADR-030 KSeF indirection model + operator setup guide#1307

Draft
norbert-kulus-blockydevs wants to merge 16 commits into
mainfrom
1283-infakt-adr-setup-guide
Draft

docs(infakt): ADR-030 KSeF indirection model + operator setup guide#1307
norbert-kulus-blockydevs wants to merge 16 commits into
mainfrom
1283-infakt-adr-setup-guide

Conversation

@norbert-kulus-blockydevs

Copy link
Copy Markdown
Collaborator

Summary

  • Adds ADR-030 (docs/architecture/adrs/030-infakt-ksef-indirection.md) documenting why InfaktInvoicingAdapter implements RegulatoryStatusReader (not RegulatoryTransmitter): inFakt auto-submits to KSeF on its own timing via its own native integration, so OL has no submit primitive to call — it only ever reads clearance status back via GET /invoices/{uuid}.json.
  • Adds the operator setup guide (docs/integrations/infakt/setup-guide.md): prerequisites, guided connection creation, webhook configuration (URL pattern, HMAC secret, verification handshake), a full verification walkthrough, and a troubleshooting table — illustrated with the 20 real screenshots already captured on this branch for feat(web): inFakt FE plugin — connection setup + invoice detail + KOR correction #1300's E2E walkthrough (libs/integrations/infakt/docs/assets/), including the inFakt-dashboard side (login, API key page, webhooks list, webhook subscription form, invoice-confirmed).
  • Adds libs/integrations/infakt/README.md — the package README this adapter was missing, mirroring the shape of the other integration READMEs added in docs(integrations): package READMEs + operator tutorials for KSeF and Subiekt nexo #1284 (adapter key, capabilities, credentials/config shape, notable implementation details).
  • Updates docs/architecture-overview.md § 14 Invoicing with a "Second provider" paragraph for inFakt, alongside the existing KSeF entry.
  • Adds the implementation plan (docs/plans/implementation-plan-infakt-docs-adr.md) per the repo's /plan workflow.

Why this PR targets 1282-infakt-fe-plugin, not main

The setup guide documents connection-wizard fields and webhook wiring that only exist on 1282-infakt-fe-plugin (which carries #1280's hardened adapter, #1281's registration + webhook routing, and #1282's FE plugin) — none of that has merged to main yet. Stacking this PR keeps its diff docs-only; it'll retarget to main automatically once 1282-infakt-fe-plugin merges.

Known gap, documented not hidden

The webhook-secret exchange direction (OL generates → paste into inFakt, vs. inFakt generates → paste into OL) couldn't be verified against inFakt's live dashboard UI in this session. The setup guide documents the more common HMAC-webhook UX (OL generates via Rotate webhook secret) and calls out the fallback + the fact that OL currently has no endpoint to set the webhook secret to an arbitrary caller-supplied value — only to rotate to a new random one.

Test plan

Part of #1279. Closes #1283.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JvfZGWAWWqVeJsFZbH1atL

@piotrswierzy piotrswierzy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tech-lead review (docs-only, reviewed against stacked base 1282-infakt-fe-plugin; diff = origin/1282...origin/1283).

Overall a well-structured docs set: ADR-030 is the next-free number, conforms to the ADR template (all required sections present), the ADR-README index row is added, all 20 referenced screenshots are committed and their paths resolve, relative links resolve, and no secrets are leaked. Adapter facts largely check out against the merged code (adapterKey infakt.accounting.v1, platformType infakt, displayName Infakt Accounting API v3, InvoicingPort + RegulatoryStatusReader + CorrectionIssuer with RegulatoryTransmitter deliberately absent, the ksef_data.status → RegulatoryStatus mapping, document types, gross→net + before/after correction rows).

One blocking accuracy issue plus a merge-ordering constraint:

🔴 BLOCKING — ADR-030's "no submit primitive" premise is contradicted by the shipped adapter and the POC it cites.
The ADR states "There is nothing for OL to submit … OL has no submit primitive to call at all" and that inFakt "auto-triggers KSeF submission the moment POST /invoices.json succeeds (confirmed live on sandbox, #1274: … zero further OL action)." But:

  • infakt-invoicing.adapter.ts ships a public sendToKsef(invoiceUuid)POST invoices/{uuid}/send_to_ksef.json.
  • The cited POC (scripts/poc-sandbox-test.ts) runs issueInvoice → getInvoice → getClearanceStatus ("pre-submit clearance") → sendToKsef ("STEP 5 — trigger KSeF submission") → cleared — it explicitly invokes a submit primitive and checks a pre-submit status, rather than proving "zero further OL action."
  • The Alternatives section says the OL-driven-submission fallback is "not modeled in code," yet sendToKsef models exactly that. (Note: #1293's rebased adapter now calls sendToKsef inline at issuance — so OL does drive submission.)

The decision (don't expose RegulatoryTransmitter) is probably still right, but the justification as written will mislead a maintainer who greps and finds sendToKsef. Please reword: OL retains an out-of-port sendToKsef trigger and does not surface it as a RegulatoryTransmitter method because clearance timing/status ownership stays with inFakt — rather than "there is no submit primitive."

🟠 IMPORTANT — merge ordering. ADR-030 and the README reference InfaktInboundWebhookDecoderAdapter, which does not exist on this PR's base (1282) — it lives on 1281-infakt-plugin-registration-webhook (#1293), and 1281 is not an ancestor of 1282. The docs correctly describe the merged end-state, but this PR can't land until #1293 and the 1282 base are on main. Confirm the class names still match after the stack collapses.

🟡 SUGGESTION — setup-guide §3 step 3 says the invoice "updates to accepted". The adapter historically mapped successcleared; #1293 remaps it to accepted. Reconcile the guide with whichever value ships, and keep it consistent with ADR-030.

🟡 SUGGESTION — README names both InfaktWebhookTranslator and InfaktInboundWebhookDecoderAdapter; the final (#1293) code has three distinct webhook classes. Tighten the prose to name which class does what.

Draft looks close — main ask is the ADR premise reword; the rest is post-stack verification.

…hots

Part of the E2E evidence trail for PR #1300 / issue #1282: inFakt
sandbox dashboard login, API key generation page, and the webhook
creation flow (list + new-webhook form). Captured manually against
the real inFakt sandbox dashboard, no secrets visible in frame.

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
…KOR, list)

Captured against the real inFakt sandbox on a temporary paired stack
(this branch's web build + the 1281-infakt-plugin-registration-webhook
backend branch), confirming the full connect -> issue -> KSeF-clearance
-> correction flow works end to end through OpenLinker:

- 00-05: guided connection setup, Test connection (passing, after the
  ConnectionTesterPort fix landed on PR #1293), connections list
- 12: invoice accepted with real KSeF clearance number
  (8201194127-20260701-A5797F400000-ED)
- 14-16: KOR correction flow, also cleared by the real sandbox
- 17: invoices list showing the original + correction, both accepted
- if1-if4: manual inFakt-dashboard screenshots (API key, webhooks)

Known gaps (tracked, not blocking):
- 13-invoice-detail-page was captured via the /invoices/:invoiceId page,
  but that GET route doesn't exist yet on the 1281 backend branch's base
  main snapshot — will be trivial to recapture once #1292/#1293 land on
  current main.
- The not-issued / submitted (pending) order-detail states aren't
  captured cleanly yet — the seeded test order is now terminal
  (accepted) for this connection; a second seeded order would be needed
  for a clean before/during capture.

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
…ng on this backend branch, not blocking)

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
…rified

Re-ran the full sandbox walkthrough on a fresh order (PLN 189.00) after
the groszy/decimal-string fix landed on PR #1293 (651cac2). Confirmed
against inFakt's raw API response that amounts now round-trip exactly
(gross_price: 18900 groszy = PLN 189.00) and the KOR correction combines
original + corrected lines correctly (189.00 + 99.99 = 288.99 PLN).

Also adds the invoice-detail-page screenshot (13) that 404'd on a stale
branch snapshot last time — the route works fine now.

Replaces the earlier PLN 349.00 test order's screenshots, which
(correctly, at the time) showed the ~100x-low amounts that led to the
money-format bug report on PR #1293.

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
…ssets/

Aligns with the convention established in PR #1284 (KSeF/Subiekt
tutorials) — screenshot assets live inside the integration package's
own docs/assets/, not a root-level docs/assets/<provider>/ directory.
Updates the two e2e scripts' output path accordingly.

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
… 6 confirmation

- 08-orders-list.png: clean, filtered (sourceConnectionId + createdFrom)
  view of just the seeded test orders — the unfiltered list is too
  cluttered with other sessions' dev-DB fixtures to be tutorial-usable.
- 10/11: genuinely clean not-issued -> submitted transition, captured
  without a page reload wiping the connection-picker selection (fixed
  infakt-invoice.mjs to shoot the submitted state before any reload).
- if5-infakt-invoice-confirmed.png: fresh manual inFakt-dashboard
  screenshot confirming the money-format fix (9/07/2026 = PLN 189.00,
  10/07/2026 correction = PLN 288.99), replacing the pre-fix evidence.
- 13/17 updated: also documents the KSeF cleared-vs-accepted mapping bug
  found this run (flagged on PR #1293) — two rows show "KSeF: CLEARING"
  (the real reconcile job's output, unpatched) alongside two "KSeF:
  ACCEPTED" rows from earlier runs where I'd manually corrected the DB
  while chasing the money bug. Will re-capture once the accepted-mapping
  fix lands.

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
…erified

Fourth fresh sandbox order (PLN 259.00), confirming both backend fixes
together: gross_price round-trips exactly (25900 groszy = PLN 259.00),
and the invoice now shows "KSeF: ACCEPTED" with a real clearance
reference chip instead of getting stuck on "KSeF: CLEARING" forever.
Correction verified too (259.00 + 99.99 = 358.99 PLN exact, gross_price
35899 groszy).

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
Documents why InfaktInvoicingAdapter implements RegulatoryStatusReader
(not RegulatoryTransmitter) — inFakt auto-submits to KSeF on its own,
so OL only ever reads clearance status back. Adds the operator setup
guide (connection creation, webhook configuration, troubleshooting),
the package README the infakt adapter was missing, and the
architecture-overview.md provider entry.

Part of #1279. Closes #1283.

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
@norbert-kulus-blockydevs norbert-kulus-blockydevs force-pushed the 1283-infakt-adr-setup-guide branch from 9de602f to b0d61a9 Compare July 3, 2026 10:13
@norbert-kulus-blockydevs norbert-kulus-blockydevs changed the base branch from 1282-infakt-fe-plugin to main July 3, 2026 10:13
…tup guide

Live-E2E-verified against the inFakt sandbox (2026-07-03):
- section 1: Default payment method wizard field + Transfer bank-account
  behaviour (live picker on the Edit form, eager persist, inFakt default
  sync) with fresh wizard + edit-form screenshots
- section 3: Download the invoice PDF step (rendered PDF via
  RegulatoryDocumentReader, #1321)
- corrections: issuance-time line snapshot note (#1297)
- README: BankAccountsReader / BankAccountDefaultSetter /
  RegulatoryDocumentReader notes + implementation details
- capability-panel screenshot (post-#1320)

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>

@piotrswierzy piotrswierzy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/pr-review — systematic review

Verdict: ❌ Request changes (consistent with draft status). Well-researched docs PR — ADR structure, link hygiene, and most technical claims check out against the code — but four accuracy defects need fixing before merge. All are small and mechanical.

Stacking status verified: the PR now targets main and the diff contains only docs (6 markdown files + 12 PNGs, no stacked code); the prereqs (#1293/#1300/#1309) have merged. Branch is behind main (#1297/#1329 landed since) — needs an update, no conflict expected.

🟡 IMPORTANT

  1. docs/architecture/adrs/030-infakt-ksef-indirection.md:43 — wrong terminal status mapping. The Decision section maps ksef_data.status onto RegulatoryStatus as submitted | submitted | cleared | rejected. The shipped adapter (infakt-invoicing.adapter.ts:73-81) maps success → 'accepted' — and its own comment documents that a cleared mapping was a past bug. The ADR is the durable record and would re-canonize the exact mistake the code warns against; it also contradicts the setup guide's own verification step 3 ("updates to accepted"). One-word fix: submitted | submitted | accepted | rejected.

  2. Setup guide (Capabilities table :23, bank-picker paragraphs ~:76-90) + README (capabilities row, #1310 bullet) — documents unmerged functionality as shipped. BankAccountsReader / BankAccountDefaultSetter don't exist on main — the adapter implements InvoicingPort, RegulatoryStatusReader, CorrectionIssuer, RegulatoryDocumentReader only, and InfaktConnectionConfig has no bankAccount field. The bank-picker walkthrough (with screenshots 06/07) describes UI an operator on main cannot find. Either sequence this PR after #1310 merges, or strip the bank-account rows/paragraphs/screenshots into #1310's own docs.

  3. Setup guide § 2, step 5 (~:128) — the OL-side "Rotate webhook secret" affordance does not exist in the FE. The backend endpoint exists (POST /connections/:id/webhooks/secret/rotate, secret shown once — that part is accurate), but a repo-wide search of apps/web/src finds no caller and no such label; the inFakt credentials-panel rotate affordance rotates the API key, not the webhook secret. An operator following the step hits a dead end. Document the API call (curl/HTTP snippet) until an FE affordance ships, or land that affordance first.

  4. Setup guide § 2, step 5 vs its own screenshot — the primary secret-exchange instruction is contradicted by if4-infakt-webhook-form.png. The embedded screenshot of inFakt's "Nowy webhook" form shows no secret field at all (URL, description, payload option, event checkboxes, account password). The guide instructs "Paste that same value into inFakt's webhook subscription secret field" as the primary path. The PR body honestly flags this as unverified — good — but the guide text asserts as fact a UI element its own screenshot disproves. Invert the framing: lead with what the screenshot shows, state the exchange direction is unverified, keep the fallback as the likely path (or verify against inFakt's webhook docs first).

🟢 SUGGESTIONS

  • libs/integrations/infakt/README.md (~:40) — the Config JSON block omits defaultPaymentMethod, which InfaktConnectionConfig carries since #1309; add it so the documented shape matches the type.
  • docs/plans/implementation-plan-infakt-docs-adr.md — plan constraints are stale (still says the PR targets 1282-infakt-fe-plugin, that #1281/#1282 are open, "no new screenshots" while four are added). A one-paragraph retarget addendum would prevent confusion.

✅ Verified-correct

  • ADR-030 matches the ADR template exactly (header, Context, Decision, 3 Alternatives with rejection rationale, Consequences, References) and is indexed in the ADR README.
  • All 24 screenshot references resolve (19 pre-existing from #1300, 4 new, + platform picker); all relative links and anchors resolve, including ADR-026's #amendments.
  • Manifest facts accurate: infakt.accounting.v1, platformType infakt, capability Invoicing.
  • Core claims coherent: RegulatoryStatusReader-not-RegulatoryTransmitter matches the code; document types, X-Infakt-Signature HMAC + verification_code handshake + send_to_ksef_* allowlist all match; RegulatoryDocumentReader (#1321) is on main; the not-applicable-forever consequence matches toRegulatoryStatus(null).
  • No credentials leaked — placeholders only; screenshots show masked fields.
  • The known-gap honesty (webhook-secret direction) is the right posture — only the primary-path framing overreaches (finding 4).

… reality check

- ADR-030: fix ksef_data.status mapping (success -> accepted, not cleared) -
  the ADR was re-canonizing exactly the bug the shipped adapter comment
  warns against
- bank-picker docs (BankAccountsReader/BankAccountDefaultSetter, #1310)
  now describe a MERGED feature - #1310 landed to main 2026-07-03,
  after Piotr's review; no doc changes needed, kept as-is post branch update
- setup-guide step 5: replace the nonexistent "Rotate webhook secret" FE
  button with the actual API call (curl snippet against
  POST /v1/connections/:id/webhooks/secret/rotate); invert the
  secret-exchange framing to lead with what if4's screenshot actually
  shows (no secret field) and mark the paste-into-inFakt direction
  explicitly unverified
- README: add defaultPaymentMethod + bankAccount to the Config JSON
  example (InfaktConnectionConfig carries both since #1309/#1310)
- plan: retarget addendum noting the branch now targets main with all
  prereqs merged
- merged origin/main (branch was behind #1297/#1329/#1310/#1320/#1331)

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
@norbert-kulus-blockydevs

Copy link
Copy Markdown
Collaborator Author

Pushed 999dda5 addressing all four IMPORTANT findings + both suggestions:

  1. ADR-030 mapping fixed to submitted | submitted | accepted | rejected (matches the shipped adapter and its own anti-regression comment).
  2. Bank-picker docs: BankAccountsReader/BankAccountDefaultSetter and the bank picker are no longer "unmerged" - feat(infakt): bank-account picker with live inFakt default sync (#1303 follow-up) #1310 merged to main on 2026-07-03, after your review. Merged origin/main into this branch; the docs now describe shipped code as-is, no strip needed.
  3. Step 5 rewritten: dropped the nonexistent "Rotate webhook secret" FE button, replaced with the actual API call (POST /v1/connections/:id/webhooks/secret/rotate, curl snippet).
  4. The secret-exchange framing now leads with what if4's screenshot actually shows (no secret field on inFakt's form) and marks the paste-direction explicitly unverified, per your finding.

Also: README config example now includes defaultPaymentMethod/bankAccount, and the plan doc has a retarget addendum noting the branch/prereq history.

The earlier review-fix commit (999dda5) addressed the /pr-review's four
numbered findings but missed the tech-lead draft review's BLOCKING finding:
ADR-030, the setup guide, and the README all claimed OL "has no submit
primitive to call" and that inFakt "auto-triggers" KSeF submission on its
own. The shipped adapter's own docstring says otherwise: an inFakt draft
does NOT auto-submit on its own, so issueInvoice/issueCorrection call
send_to_ksef.json explicitly and inline - verified live 2026-07-01.

Reworded throughout to the framing the reviewer suggested: OL retains an
out-of-port sendToKsef trigger, not surfaced as RegulatoryTransmitter,
because clearance timing and status ownership stay with inFakt - not
because there is nothing for OL to call. Also disambiguates the three
webhook classes in the README (InfaktWebhookTranslator,
InfaktInboundWebhookDecoderAdapter, InfaktWebhookEventTranslatorAdapter),
per the same draft review's suggestion, which the prior commit also left
unaddressed.

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
@norbert-kulus-blockydevs

Copy link
Copy Markdown
Collaborator Author

Pushed e304219, addressing the tech-lead draft review's BLOCKING finding that the earlier fix commit (999dda5) missed.

The draft review flagged that ADR-030's premise — "there is nothing for OL to submit ... zero further OL action" — is contradicted by the shipped adapter: InfaktInvoicingAdapter ships a public sendToKsef(invoiceUuid) that issueInvoice/issueCorrection call inline, and the adapter's own docstring says an inFakt draft does not auto-submit on its own. That claim had propagated into the setup guide (Capabilities table + Prerequisites) and the README as well.

Reworded throughout, following the reviewer's suggested framing verbatim: OL retains an out-of-port sendToKsef trigger, not surfaced as a RegulatoryTransmitter method, because clearance timing and status ownership stay with inFakt — not because there is nothing to call. Concretely:

  • ADR-030: Context, Decision, Alternatives, and Consequences sections all reworded to describe the real flow (OL explicitly triggers send_to_ksef.json inline; inFakt then owns the KSeF session/XML/status from that point on).
  • Setup guide: Capabilities table row and Prerequisites bullet 2 reworded to match; the two Troubleshooting rows referencing an "auto-submit toggle" tightened to match (also dropped the leftover cleared/accepted inconsistency in the "stays submitted forever" row).
  • README: same reword in the RegulatoryTransmitter-not-implemented paragraph and the "KSeF-as-intermediary" implementation-detail bullet.
  • README: also addressed the draft review's suggestion that was still open — disambiguated the three actual webhook classes (InfaktWebhookTranslator, InfaktInboundWebhookDecoderAdapter, InfaktWebhookEventTranslatorAdapter) instead of naming only two.

Everything from the second (/pr-review) pass — the accepted mapping fix, the bank-picker docs, the webhook-secret reality check, the README config example, and the plan-doc retarget addendum — was already correctly addressed in 999dda5 and is unchanged here.

@norbert-kulus-blockydevs

Copy link
Copy Markdown
Collaborator Author

Combined review: /tech-review + /pr-review

Two independent passes (tech-lead review + systematic PR review) were run against the shipped libs/integrations/infakt/ source. They converged on the same single blocking issue. Docs-only PR, so the dominant axis was factual accuracy of the claims vs the code that actually ships.

Summary

High-quality, unusually well-researched docs set. The ADR-030, README, and setup guide are overwhelmingly accurate against the shipped InfaktInvoicingAdapter: the implements clause (6 capabilities, no RegulatoryTransmitter), the inline send_to_ksef.json submit model, the ksef_data.status -> RegulatoryStatus mapping, the config shape, the three webhook classes, the webhook HMAC/verification handshake, the rotate-secret endpoint, ADR template + index registration, all screenshot paths, and all doc links all check out. The one material problem: the single line this PR adds to architecture-overview.md § 14 contradicts the very ADR it ships alongside (and the code) by re-asserting the stale "auto-submit / no submit primitive" framing that commit e304219 corrected everywhere else.

Issues

[BLOCKING] - docs/architecture-overview.md (§ 14, new "Second provider" line)

The line states: "inFakt auto-triggers KSeF submission on its own - OL never opens a KSeF session itself. InfaktInvoicingAdapter implements ... RegulatoryStatusReader (not RegulatoryTransmitter - there is no submit primitive for OL to call)". Both claims are factually wrong and contradict this same PR's ADR-030. The adapter DOES call an explicit submit primitive: sendToKsef() at infakt-invoicing.adapter.ts:498 POSTs invoices/{uuid}/send_to_ksef.json, invoked inline by issueInvoice (:289) and issueCorrection (:465). The docstring (:9-10) is explicit: "an Infakt draft does NOT auto-submit to KSeF on its own". ADR-030 says the same. The real reason it declares RegulatoryStatusReader rather than RegulatoryTransmitter is submission timing/ownership staying with inFakt, not the absence of a primitive. Reword to match ADR-030/README/setup-guide: OL explicitly triggers submission via an inline send_to_ksef.json call, but inFakt owns the KSeF session/FA(3) XML/clearance timing, hence status-reader not transmitter.

[IMPORTANT] - PR description (Summary, first bullet) and docs/plans/implementation-plan-infakt-docs-adr.md (Context)

Both repeat the same stale claim ("inFakt auto-submits ... OL has no submit primitive to call - it only ever reads clearance status"). Both were touched recently (the plan carries a 2026-07-06 retarget addendum), so they should be corrected to the inline-send_to_ksef.json framing rather than shipped contradicting the ADR in the same PR. Anyone keying off the PR summary gets the wrong mental model.

[IMPORTANT] - libs/integrations/infakt/README.md (Config JSON example)

Example shows "baseUrl": "https://api.infakt.pl", but the real default is INFAKT_DEFAULT_BASE_URL = 'https://api.infakt.pl/api/v3' (infrastructure/http/infakt-http-client.ts:20) and a supplied baseUrl is used verbatim with no path appended (:37). An operator copying the sample into config would 404 on every request (missing /api/v3). The prose is fine (omit for production); fix the example value to include /api/v3 or make it an explicit sandbox host.

[SUGGESTION] - docs/integrations/infakt/setup-guide.md (Troubleshooting, "submitted forever" row)

Accurate but dense. Consider splitting "KSeF rejected the doc" vs "KSeF integration disabled" into two rows for scannability.

Positive observations (verified)

  • Screenshot integrity clean: all referenced images resolve; the ones not added by this PR exist on origin/main (from feat(web): inFakt FE plugin — connection setup + invoice detail + KOR correction #1300), so nothing breaks on merge; zero orphan assets.
  • node scripts/check-repo-urls.mjs passes.
  • ADR-030 follows the template, Status: Accepted, correctly registered in docs/architecture/adrs/README.md with matching number/title/date.
  • Rotate-secret curl is correct: route /v1/connections/{id}/webhooks/secret/rotate (URI versioning, no global prefix) and the { secret, revealedOnce, warning } response shape both match controller + DTO.
  • The "Known gap" caveat (no OL endpoint to set a caller-supplied webhook secret; rotate-only, server-generated) is accurate against RotateWebhookSecretResponseDto.

Verdict: 🔄 Approve with changes

Priority fixes:

  1. (BLOCKING) Rewrite the architecture-overview.md § 14 line so it no longer claims inFakt "auto-triggers" submission or that there is "no submit primitive for OL to call" - align with ADR-030/README/setup-guide.
  2. (IMPORTANT) Correct the same stale claim in the PR description's first bullet and in the implementation plan's Context section.
  3. (IMPORTANT) Fix the README baseUrl example to include /api/v3 (or mark sandbox-only).
  4. (SUGGESTION) Split the troubleshooting row.

Everything else is accurate and well-sourced - fix the § 14 line and the two echoes of it and this is mergeable.

@norbert-kulus-blockydevs norbert-kulus-blockydevs marked this pull request as ready for review July 6, 2026 08:09
@norbert-kulus-blockydevs norbert-kulus-blockydevs marked this pull request as draft July 6, 2026 10:56

@piotrswierzy piotrswierzy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/pr-review — delta re-review (161ab4afe30421988, docs-only)

All four findings from my earlier Request-changes are resolved. Verified against the actual files, not the PR body.

  1. ADR-030 wrong terminal status mapping → RESOLVED. Now reads submitted | submitted | accepted | rejected with explicit prose "success maps to the terminal accepted state, not cleared" — matches the shipped adapter (infakt-invoicing.adapter.ts case 'success': return 'accepted').
  2. #1310 bank-account functionality documented as shipped → MOOT (now merged). #1310 landed on main, the branch merged it, and the adapter implements the six capabilities the README table lists (InvoicingPort, RegulatoryStatusReader, CorrectionIssuer, RegulatoryDocumentReader, BankAccountsReader, BankAccountDefaultSetter). Docs match main.
  3. Nonexistent "Rotate webhook secret" FE affordance → RESOLVED. Replaced with the real API call POST /v1/connections/{id}/webhooks/secret/rotate (confirmed at connection.controller.ts:223; response DTO matches). The doc's claim that there's no inFakt-connection FE webhook-secret affordance (unlike PrestaShop/Erli) is accurate.
  4. Secret-exchange framing contradicted by its own screenshot → RESOLVED. Step 5 now leads with "inFakt's 'Nowy webhook' form has no secret field" (matching if4-…) and the Known Gap honestly discloses OL can't set its secret to inFakt's auto-generated value.

Also verified: README config JSON (baseUrl/defaultPaymentMethod/bankAccount{id,accountNumber,bankName}) matches InfaktConnectionConfig exactly and correctly excludes apiKey (a credential) — no config/secret drift; ADR-030 indexed as Accepted; asset links resolve; no unredacted secrets in text.

🟢 One minor wording nit (non-blocking)

Setup guide step 5 (~lines 129-130) still carries an actionable imperative — "set it as OL's webhook secret so X-Infakt-Signature verification matches" — while the Known Gap block immediately below says there's currently no mechanism to do that. The gap is disclosed adjacently, but soften the imperative (e.g. "you would set it — but note there's currently no mechanism; see Known Gap") so it doesn't tell the operator to do something the next paragraph calls impossible.

Verdict: ✅ Approve (docs-only). All Request-changes findings addressed; the wording nit is optional.

norbert-kulus-blockydevs added a commit that referenced this pull request Jul 7, 2026


PR #1307 already claims ADR-030 for the inFakt/KSeF indirection model.
Renumbering ours to the next free slot before it merges.

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
norbert-kulus-blockydevs added a commit that referenced this pull request Jul 7, 2026
…ible signal

connection.supportedCapabilities turned out to be a static, per-adapterKey
manifest value rather than computed per-connection-instance, so it can't
distinguish an Erli connection with configured Allegro app credentials from
one without. Add a non-secret ErliConnectionConfig.allegroCategoryAccessEnabled
boolean for the frontend to read instead (see ADR-031 "Correction"); the
write path that sets allegroClientId/allegroClientSecret must set/clear this
flag atomically (tracked in #1384's scope).

Also fixes stale ADR-030 references (renumbered to ADR-031 to avoid a
collision with #1307) in allegro-category-catalog-client.ts and
erli-connection.types.ts.

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
piotrswierzy pushed a commit that referenced this pull request Jul 7, 2026
…lient-credentials token (#1380)

* docs(plan): Erli category/parameter browsing via Erli-owned Allegro client-credentials token

Implementation plan + ADR-030 for letting the Erli offer wizard browse
Allegro's public category/parameter catalog (client_credentials grant)
without requiring the operator to connect a real Allegro seller account.

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>

* docs(plan): renumber ADR-030 to ADR-031 to avoid collision with PR #1307

PR #1307 already claims ADR-030 for the inFakt/KSeF indirection model.
Renumbering ours to the next free slot before it merges.

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>

* docs(plan): correct FE gating signal — supportedCapabilities is static, use config flag

#1383 implementation discovered connection.supportedCapabilities is a
static, per-adapterKey manifest value, not computed per-connection
instance — it can't distinguish configured vs unconfigured Erli
connections. Corrected ADR-031 and the plan to use a new non-secret
ErliConnectionConfig.allegroCategoryAccessEnabled boolean instead,
written/cleared alongside the Allegro credential pair.

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>

---------

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
norbert-kulus-blockydevs added a commit that referenced this pull request Jul 7, 2026
… credential/config validation (#1399)

* feat(erli): wire per-connection Allegro category-catalog capability + credential/config validation (#1383)

ErliOfferManagerAdapter now wires fetchCategories/fetchCategoryParameters as
optional instance properties (never a static implements clause) so
isCategoryBrowser/isCategoryParametersReader reflect whether a specific Erli
connection has configured a valid Allegro app credential pair, per ADR-031.
ErliAdapterFactory constructs the shared AllegroCategoryCatalogClient only
when both allegroClientId and allegroClientSecret resolve to non-empty
strings, resolving config.allegroEnvironment (default 'production').

The credentials shape validator now enforces "both or neither" for the
Allegro credential pair, and the config shape validator restricts
allegroEnvironment to 'sandbox' | 'production'.

Verified CategoriesCacheService and ListingsController need no changes: both
already resolve capabilities generically via getCapabilityAdapter + is* type
guards, so a misconfigured Erli connection behaves like today's
"adapter doesn't implement this capability" case.

Added an integration test exercising the real ErliAdapterFactory +
ErliOfferManagerAdapter + AllegroCategoryCatalogClient (fetch stubbed) through
the production adapter-resolution seam and the real HTTP endpoint, covering
configured / unconfigured / partially-configured connections. Confirmed the
existing #1367 bulk-wizard capability-gate test passes unmodified.

Part of #1381, sub-task 2 of 4 (depends on #1382, merged; see also #1384, #1387).

Signed-off-by: Norbert Kulus <42zeroo@gmail.com>
Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>

* fix(erli): add allegroCategoryAccessEnabled config flag as the FE-visible signal

connection.supportedCapabilities turned out to be a static, per-adapterKey
manifest value rather than computed per-connection-instance, so it can't
distinguish an Erli connection with configured Allegro app credentials from
one without. Add a non-secret ErliConnectionConfig.allegroCategoryAccessEnabled
boolean for the frontend to read instead (see ADR-031 "Correction"); the
write path that sets allegroClientId/allegroClientSecret must set/clear this
flag atomically (tracked in #1384's scope).

Also fixes stale ADR-030 references (renumbered to ADR-031 to avoid a
collision with #1307) in allegro-category-catalog-client.ts and
erli-connection.types.ts.

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>

* fix(erli): hoist Allegro category-token cache to host.cache + dedupe credential resolve (#1399 review)

AllegroCategoryCatalogClient built a fresh in-memory token cache per adapter
instance, but ErliAdapterFactory builds a fresh instance per
getCapabilityAdapter call — so fetchCategoryParameters paid a full
client_credentials OAuth round-trip on every request. The client now
persists the acquired token in the optional CachePort (host.cache), keyed by
clientId, with a TTL matching the token's remaining lifetime, so it survives
across per-request instances and processes.

Also collapses ErliAdapterFactory.createAdapters's two credentialsResolver.get
calls (one for apiKey, one for the Allegro pair) into one shared resolve.

Addresses both IMPORTANT findings from the PR #1399 review round (Piotr's
token-cache note and the self-review's double-resolve finding).

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>

---------

Signed-off-by: Norbert Kulus <42zeroo@gmail.com>
Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
norbert-kulus-blockydevs added a commit that referenced this pull request Jul 7, 2026
… credential/config validation (#1399)

* feat(erli): wire per-connection Allegro category-catalog capability + credential/config validation (#1383)

ErliOfferManagerAdapter now wires fetchCategories/fetchCategoryParameters as
optional instance properties (never a static implements clause) so
isCategoryBrowser/isCategoryParametersReader reflect whether a specific Erli
connection has configured a valid Allegro app credential pair, per ADR-031.
ErliAdapterFactory constructs the shared AllegroCategoryCatalogClient only
when both allegroClientId and allegroClientSecret resolve to non-empty
strings, resolving config.allegroEnvironment (default 'production').

The credentials shape validator now enforces "both or neither" for the
Allegro credential pair, and the config shape validator restricts
allegroEnvironment to 'sandbox' | 'production'.

Verified CategoriesCacheService and ListingsController need no changes: both
already resolve capabilities generically via getCapabilityAdapter + is* type
guards, so a misconfigured Erli connection behaves like today's
"adapter doesn't implement this capability" case.

Added an integration test exercising the real ErliAdapterFactory +
ErliOfferManagerAdapter + AllegroCategoryCatalogClient (fetch stubbed) through
the production adapter-resolution seam and the real HTTP endpoint, covering
configured / unconfigured / partially-configured connections. Confirmed the
existing #1367 bulk-wizard capability-gate test passes unmodified.

Part of #1381, sub-task 2 of 4 (depends on #1382, merged; see also #1384, #1387).

Signed-off-by: Norbert Kulus <42zeroo@gmail.com>
Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>

* fix(erli): add allegroCategoryAccessEnabled config flag as the FE-visible signal

connection.supportedCapabilities turned out to be a static, per-adapterKey
manifest value rather than computed per-connection-instance, so it can't
distinguish an Erli connection with configured Allegro app credentials from
one without. Add a non-secret ErliConnectionConfig.allegroCategoryAccessEnabled
boolean for the frontend to read instead (see ADR-031 "Correction"); the
write path that sets allegroClientId/allegroClientSecret must set/clear this
flag atomically (tracked in #1384's scope).

Also fixes stale ADR-030 references (renumbered to ADR-031 to avoid a
collision with #1307) in allegro-category-catalog-client.ts and
erli-connection.types.ts.

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>

* fix(erli): hoist Allegro category-token cache to host.cache + dedupe credential resolve (#1399 review)

AllegroCategoryCatalogClient built a fresh in-memory token cache per adapter
instance, but ErliAdapterFactory builds a fresh instance per
getCapabilityAdapter call — so fetchCategoryParameters paid a full
client_credentials OAuth round-trip on every request. The client now
persists the acquired token in the optional CachePort (host.cache), keyed by
clientId, with a TTL matching the token's remaining lifetime, so it survives
across per-request instances and processes.

Also collapses ErliAdapterFactory.createAdapters's two credentialsResolver.get
calls (one for apiKey, one for the Allegro pair) into one shared resolve.

Addresses both IMPORTANT findings from the PR #1399 review round (Piotr's
token-cache note and the self-review's double-resolve finding).

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>

---------

Signed-off-by: Norbert Kulus <42zeroo@gmail.com>
Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
norbert-kulus-blockydevs added a commit that referenced this pull request Jul 8, 2026
… credential/config validation (#1399)

* feat(erli): wire per-connection Allegro category-catalog capability + credential/config validation (#1383)

ErliOfferManagerAdapter now wires fetchCategories/fetchCategoryParameters as
optional instance properties (never a static implements clause) so
isCategoryBrowser/isCategoryParametersReader reflect whether a specific Erli
connection has configured a valid Allegro app credential pair, per ADR-031.
ErliAdapterFactory constructs the shared AllegroCategoryCatalogClient only
when both allegroClientId and allegroClientSecret resolve to non-empty
strings, resolving config.allegroEnvironment (default 'production').

The credentials shape validator now enforces "both or neither" for the
Allegro credential pair, and the config shape validator restricts
allegroEnvironment to 'sandbox' | 'production'.

Verified CategoriesCacheService and ListingsController need no changes: both
already resolve capabilities generically via getCapabilityAdapter + is* type
guards, so a misconfigured Erli connection behaves like today's
"adapter doesn't implement this capability" case.

Added an integration test exercising the real ErliAdapterFactory +
ErliOfferManagerAdapter + AllegroCategoryCatalogClient (fetch stubbed) through
the production adapter-resolution seam and the real HTTP endpoint, covering
configured / unconfigured / partially-configured connections. Confirmed the
existing #1367 bulk-wizard capability-gate test passes unmodified.

Part of #1381, sub-task 2 of 4 (depends on #1382, merged; see also #1384, #1387).

Signed-off-by: Norbert Kulus <42zeroo@gmail.com>
Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>

* fix(erli): add allegroCategoryAccessEnabled config flag as the FE-visible signal

connection.supportedCapabilities turned out to be a static, per-adapterKey
manifest value rather than computed per-connection-instance, so it can't
distinguish an Erli connection with configured Allegro app credentials from
one without. Add a non-secret ErliConnectionConfig.allegroCategoryAccessEnabled
boolean for the frontend to read instead (see ADR-031 "Correction"); the
write path that sets allegroClientId/allegroClientSecret must set/clear this
flag atomically (tracked in #1384's scope).

Also fixes stale ADR-030 references (renumbered to ADR-031 to avoid a
collision with #1307) in allegro-category-catalog-client.ts and
erli-connection.types.ts.

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>

* fix(erli): hoist Allegro category-token cache to host.cache + dedupe credential resolve (#1399 review)

AllegroCategoryCatalogClient built a fresh in-memory token cache per adapter
instance, but ErliAdapterFactory builds a fresh instance per
getCapabilityAdapter call — so fetchCategoryParameters paid a full
client_credentials OAuth round-trip on every request. The client now
persists the acquired token in the optional CachePort (host.cache), keyed by
clientId, with a TTL matching the token's remaining lifetime, so it survives
across per-request instances and processes.

Also collapses ErliAdapterFactory.createAdapters's two credentialsResolver.get
calls (one for apiKey, one for the Allegro pair) into one shared resolve.

Addresses both IMPORTANT findings from the PR #1399 review round (Piotr's
token-cache note and the self-review's double-resolve finding).

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>

---------

Signed-off-by: Norbert Kulus <42zeroo@gmail.com>
Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
piotrswierzy pushed a commit that referenced this pull request Jul 8, 2026
…t a required Allegro connection (#1407)

* feat(erli): Erli-owned Allegro client_credentials category-catalog client (#1388)

* feat(erli): add Erli-owned Allegro client_credentials category-catalog client

Adds AllegroCategoryCatalogClient, a self-contained HTTP client that lets
an Erli connection browse Allegro's public /sale/categories and
/sale/categories/{id}/parameters catalog via an Allegro app's
grant_type=client_credentials token, with no dependency on
@openlinker/integrations-allegro (per ADR-030) and no requirement for the
operator to own a real Allegro seller connection.

Extends ErliConnectionConfig with an optional allegroEnvironment field and
ErliCredentials with optional allegroClientId/allegroClientSecret fields
to carry the Allegro app credentials alongside Erli's own apiKey.

Part of #1381, sub-task 1 of 3 (see #1383, #1384).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RpsVFTpKZ1HoowEUKzbnWu
Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>

* fix(erli): address PR #1388 review findings on AllegroCategoryCatalogClient

- Extract inline wire-shape types to allegro-category-catalog-client.types.ts,
  matching the erli-http-client.ts / erli-http-client.types.ts split.
- Add a single-flight guard around token acquisition so two concurrent
  fetchCategories/fetchCategoryParameters calls on a cold cache issue one
  grant_type=client_credentials request, not two.
- Treat a token response missing expires_in as already-expired instead of
  caching it indefinitely.
- Add tests for the 401/403 branch on a data call (categories/parameters),
  distinct from the token-endpoint rejection branch already covered.
- Add a cross-plugin mapper parity test suite that runs Allegro's real
  sandbox category-parameters fixture through this client's copy of
  toNeutralCategoryParameter, mirroring assertions from
  allegro-category-parameter.mapper.spec.ts, with cross-referencing comments
  in both spec files so a future mapper change prompts updating both.
- Replace the inline 'sandbox' | 'production' union with an as const +
  runtime-array AllegroCatalogEnvironment type in erli-connection.types.ts,
  matching AllegroConnectionConfig's own convention.

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 5 <noreply@anthropic.com>

* feat(erli): wire per-connection Allegro category-catalog capability + credential/config validation (#1399)

* feat(erli): wire per-connection Allegro category-catalog capability + credential/config validation (#1383)

ErliOfferManagerAdapter now wires fetchCategories/fetchCategoryParameters as
optional instance properties (never a static implements clause) so
isCategoryBrowser/isCategoryParametersReader reflect whether a specific Erli
connection has configured a valid Allegro app credential pair, per ADR-031.
ErliAdapterFactory constructs the shared AllegroCategoryCatalogClient only
when both allegroClientId and allegroClientSecret resolve to non-empty
strings, resolving config.allegroEnvironment (default 'production').

The credentials shape validator now enforces "both or neither" for the
Allegro credential pair, and the config shape validator restricts
allegroEnvironment to 'sandbox' | 'production'.

Verified CategoriesCacheService and ListingsController need no changes: both
already resolve capabilities generically via getCapabilityAdapter + is* type
guards, so a misconfigured Erli connection behaves like today's
"adapter doesn't implement this capability" case.

Added an integration test exercising the real ErliAdapterFactory +
ErliOfferManagerAdapter + AllegroCategoryCatalogClient (fetch stubbed) through
the production adapter-resolution seam and the real HTTP endpoint, covering
configured / unconfigured / partially-configured connections. Confirmed the
existing #1367 bulk-wizard capability-gate test passes unmodified.

Part of #1381, sub-task 2 of 4 (depends on #1382, merged; see also #1384, #1387).

Signed-off-by: Norbert Kulus <42zeroo@gmail.com>
Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>

* fix(erli): add allegroCategoryAccessEnabled config flag as the FE-visible signal

connection.supportedCapabilities turned out to be a static, per-adapterKey
manifest value rather than computed per-connection-instance, so it can't
distinguish an Erli connection with configured Allegro app credentials from
one without. Add a non-secret ErliConnectionConfig.allegroCategoryAccessEnabled
boolean for the frontend to read instead (see ADR-031 "Correction"); the
write path that sets allegroClientId/allegroClientSecret must set/clear this
flag atomically (tracked in #1384's scope).

Also fixes stale ADR-030 references (renumbered to ADR-031 to avoid a
collision with #1307) in allegro-category-catalog-client.ts and
erli-connection.types.ts.

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>

* fix(erli): hoist Allegro category-token cache to host.cache + dedupe credential resolve (#1399 review)

AllegroCategoryCatalogClient built a fresh in-memory token cache per adapter
instance, but ErliAdapterFactory builds a fresh instance per
getCapabilityAdapter call — so fetchCategoryParameters paid a full
client_credentials OAuth round-trip on every request. The client now
persists the acquired token in the optional CachePort (host.cache), keyed by
clientId, with a TTL matching the token's remaining lifetime, so it survives
across per-request instances and processes.

Also collapses ErliAdapterFactory.createAdapters's two credentialsResolver.get
calls (one for apiKey, one for the Allegro pair) into one shared resolve.

Addresses both IMPORTANT findings from the PR #1399 review round (Piotr's
token-cache note and the self-review's double-resolve finding).

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>

---------

Signed-off-by: Norbert Kulus <42zeroo@gmail.com>
Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>

* feat(erli,web): Allegro credentials checkbox + offer wizard category/parameters steps (#1401)

* feat(erli,web): checkbox-reveal Allegro credentials panel + offer wizard category/parameters steps

Adds the operator-facing half of the Erli-owned Allegro category-catalog
feature (#1384, part of epic #1381, depends on #1382/#1383):

- ErliCredentialsPanel gains a "Browse Allegro categories when creating
  Erli offers" checkbox that reveals Client ID / Client Secret fields
  (masked, show/hide toggle). Saving sequences two existing mutations from
  one click: useUpdateConnectionCredentialsMutation (credentials pair,
  merged with apiKey) then useUpdateConnectionMutation (config patch for
  allegroCategoryAccessEnabled) — credentials first so a failure never
  leaves the flag advertising access the backend can't serve.

- ErliCreateOfferWizard renders the reused Allegro CategoryPicker +
  CategoryParametersStep (fed by the existing useCategoryParametersQuery)
  as dedicated Category / Category-parameters steps when
  connection.config.allegroCategoryAccessEnabled is true — per ADR-031's
  correction, this per-connection-instance config flag is the FE gating
  signal, not the static, per-adapterKey connection.supportedCapabilities.
  Falls back to today's plain-text field + a link to the connection's edit
  page otherwise. Stepper labels and needsProductParameters become
  capability-conditional; erliCreateOfferSchema gains a parameters slice
  serialized into overrides.parameters on submit (no BE change needed).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RpsVFTpKZ1HoowEUKzbnWu
Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>

* fix(erli,web): address PR #1401 review findings

- Gate `canSubmit` in ErliCredentialsPanel on `allegroEnabled` so
  unchecking the Allegro-access box after typing (but not saving)
  Client ID/Secret disables Save instead of showing a false-positive
  "Credentials saved" toast while discarding the typed secret.
- Drop the checkbox's invalid inline `accentColor: var(--accent)`
  (token doesn't exist) — `index.css` already applies
  `accent-color: var(--accent-primary)` globally.
- Restore category-parameter values on Erli offer retry by reusing
  the Allegro retry mapper's `readParameters` heuristic (exported)
  instead of always prefilling an empty `parameters` object — both
  platforms persist the same neutral `overrides.parameters` shape.

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>

* fix(erli,connections): merge credential rotation + enforce category pick (#1401 second review)

- ConnectionService.updateCredentials now merges the submitted payload onto
  the existing stored credentialsJson instead of replacing it wholesale, so
  rotating the plain Erli apiKey no longer silently wipes a previously
  configured allegroClientId/allegroClientSecret pair (and enabling Allegro
  access without touching apiKey no longer fails shape validation).
- ErliCreateOfferWizard now blocks Next on the Category step until a
  category is actually selected, mirroring AllegroCreateOfferWizard's
  required categoryId field, instead of silently falling through to
  Category-parameters/Review with an empty category.
- Carried over the approved mockup's helper copy under the Allegro Client ID
  / Client Secret fields and restored the checkbox's original hint text.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>

* test(erli,docs): cover mutation-sequencing failure paths + close ADR-031 documentation gaps (#1401 third review)

- erli-credentials-panel.test.tsx: add regression tests asserting the config
  patch never fires when the credentials write rejects, and that a config-patch
  rejection after a successful credentials write leaves the flag/fields intact
  with a retryable inline error - the two claims the PR description makes about
  "the trickiest part of this issue" were previously unverified by the suite.
- ADR-031: add a second Correction noting the sequencing is two FE-orchestrated
  mutations, not a single backend write, since no endpoint accepts both the
  credential pair and the config flag together; also record the wizard's
  3-step-vs-5-step topology and the deferred credential-verification indicator
  as explicit, confirmed scope cuts rather than implicit deviations from the
  approved mockup.

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 Sonnet 5 <noreply@anthropic.com>

* feat(erli): reuse an existing Allegro connection's credentials for category access (#1387) (#1405)

Extends the #1384 Erli credentials panel with a reuse-vs-manual choice: when
the operator already has an Allegro connection, its clientId/clientSecret can
be copied server-side into the Erli connection instead of registering a
second Allegro app. The raw clientSecret never round-trips through the
browser.

The credential-copy logic is dispatched through a new, platform-neutral
ConnectionCredentialsRewriterPort + registry (mirroring the existing
ConnectionConfigShapeValidatorPort/ConnectionCredentialsShapeValidatorPort
pair) so the generic ConnectionService.updateCredentials stays unaware that
Allegro or Erli exist. The Erli-specific resolution lives entirely in
ErliAllegroCredentialsRewriterAdapter, registered from a companion
ErliCredentialsRewriterModule (mirrors ErliWebhookProvisioningModule) since
it needs ConnectionPort, which is intentionally outside the plugin-neutral
HostServices bag.

Closes #1387

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>

---------

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
Signed-off-by: Norbert Kulus <42zeroo@gmail.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
…or convention parity

Every other integration (allegro, dpd-polska, erli, inpost, ksef, subiekt,
woocommerce) already lives at libs/integrations/<name>/docs/setup-guide.md
after the recent doc-location convention change on main. inFakt's guide had
already moved its screenshots there but left the markdown file behind at the
old top-level docs/integrations/infakt/ location. Move the file alongside its
assets and fix every cross-reference (ADR-030, package README,
architecture-overview.md).

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
…the 4 sub-capabilities shipped since #1307's last update

InfaktInvoicingAdapter now implements 10 Invoicing sub-capabilities;
this branch's docs only covered 6 (missing RegulatoryResubmitter #1356,
PaymentStatusReader #1354, PaymentMarker #1362, InvoiceEmailSender #1353).
Also fixes a since-stale claim that invoice_marked_as_paid webhooks are
ignored (they now drive PaymentStatusReader via #1354/#1361), documents
the dedicated corrective_invoices.json endpoint (#1342), and adds the
per-connection shipping-line label override (#1517/#1562).

docs/capabilities.md and architecture-overview.md's InvoicingPort
sub-capability list had the same 6-of-10 staleness independent of this
branch - fixed alongside since they're the same underlying gap.

Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs(infakt): ADR Infakt-KSeF indirection model + operator setup guide

2 participants