Skip to content

fix: auto-create credentials for all user's companies on cert upload#110

Merged
emilwojtaszek merged 4 commits into
mainfrom
fix/auto-create-credentials-for-all-companies
Apr 1, 2026
Merged

fix: auto-create credentials for all user's companies on cert upload#110
emilwojtaszek merged 4 commits into
mainfrom
fix/auto-create-credentials-for-all-companies

Conversation

@emilwojtaszek

@emilwojtaszek emilwojtaszek commented Apr 1, 2026

Copy link
Copy Markdown
Member

Summary

  • When uploading a KSeF certificate, credentials and auth jobs are now created for all user's companies, not just the currently selected one
  • When creating a new company, a credential is auto-created if the user already has an active certificate
  • Extracts Credentials.ensure_credential_for_company/1 into the context to avoid duplicating business logic across LiveViews

Test plan

  • Unit tests for ensure_credential_for_company/1 (creates when missing, returns existing)
  • LiveView test: certificate upload creates credentials for all user's companies
  • LiveView test: new company auto-creates credential when user has certificate
  • LiveView test: new company skips credential when user has no certificate
  • All 1522 tests pass, mix credo --strict clean

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Certificate uploads now create active credentials for all companies you own, not just the currently viewed company
    • Creating a company will automatically set up credentials when you have an active user certificate
    • Authentication jobs are automatically enqueued for companies when credentials are created
  • Tests

    • Added tests validating credential creation for uploads and company creation, including multi-company scenarios

When a user uploads a KSeF certificate, credentials were only created
for the currently selected company. If the user owned multiple companies,
the others would never sync until re-uploading the same certificate.

Now certificate upload creates credentials and triggers auth for all
the user's companies. Additionally, creating a new company auto-creates
a credential if the user already has a certificate.

Extracts ensure_credential_for_company/1 into the Credentials context
to avoid duplicating business logic across LiveViews.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Apr 1, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds Credentials.ensure_credential_for_company/1 and updates certificate upload and company-creation flows to ensure credentials for all companies a user owns, enqueueing auth jobs per company; includes new Companies.list_owned_companies_for_user/1, AuthWorker.enqueue/1, and related tests.

Changes

Cohort / File(s) Summary
Credentials core
lib/ksef_hub/credentials.ex
Added ensure_credential_for_company/1 that returns an existing active credential or atomically replaces/creates one via replace_active_credential/2.
Certificate upload LiveView
lib/ksef_hub_web/live/certificate_live.ex
Replaced single-company credential creation with iteration over Companies.list_owned_companies_for_user/1; calls Credentials.ensure_credential_for_company/1 per company and enqueues auth jobs via KsefHub.KsefClient.AuthWorker.enqueue/1; simplified warning handling on failures.
Company create LiveView
lib/ksef_hub_web/live/company_live/form.ex
On successful company creation, added maybe_setup_credential/2 to check user's active certificate, call Credentials.ensure_credential_for_company(company.id), and enqueue auth job when appropriate.
Companies context
lib/ksef_hub/companies.ex
Added list_owned_companies_for_user/1 to return active companies where the user has owner membership.
Auth worker
lib/ksef_hub/ksef_client/auth_worker.ex
Added enqueue/1 to insert an Oban job with %{company_id}; logs failures but always returns :ok.
Credential tests
test/ksef_hub/credentials_test.exs
New tests for ensure_credential_for_company/1: creates when missing and returns existing when present.
LiveView tests
test/ksef_hub_web/live/certificate_live_test.exs, test/ksef_hub_web/live/company_live_test.exs
Added tests asserting credentials are created for all user-owned companies after certificate upload; company tests add Mox setup and cover create-via-form behavior with and without a user certificate.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CertificateLive as Certificate LiveView
    participant Companies as Companies Module
    participant Credentials as Credentials Module
    participant AuthWorker as AuthWorker (Oban)

    User->>CertificateLive: Upload certificate
    CertificateLive->>Companies: list_owned_companies_for_user(user.id)
    Companies-->>CertificateLive: [company1, company2, ...]
    loop per company
        CertificateLive->>Credentials: ensure_credential_for_company(company.id)
        alt credential exists
            Credentials-->>CertificateLive: {:ok, credential}
        else create credential
            Credentials->>Credentials: replace_active_credential(company.id, %{})
            Credentials-->>CertificateLive: {:ok, new_credential}
        end
        CertificateLive->>AuthWorker: enqueue(company.id)
        AuthWorker-->>CertificateLive: :ok
    end
    CertificateLive-->>User: confirm upload and enqueued auth jobs
Loading
sequenceDiagram
    participant User
    participant CompanyForm as Company Form LiveView
    participant Companies as Companies Module
    participant Credentials as Credentials Module
    participant AuthWorker as AuthWorker (Oban)

    User->>CompanyForm: Submit create company form
    CompanyForm->>Companies: create_company_with_owner(user, params)
    Companies-->>CompanyForm: {:ok, %{company: company}}
    CompanyForm->>Credentials: get_active_user_certificate(user.id)
    alt user has certificate
        Credentials-->>CompanyForm: certificate
        CompanyForm->>Credentials: ensure_credential_for_company(company.id)
        Credentials-->>CompanyForm: {:ok, credential}
        CompanyForm->>AuthWorker: enqueue(company.id)
        AuthWorker-->>CompanyForm: :ok
    else no certificate
        Credentials-->>CompanyForm: nil
    end
    CompanyForm-->>User: company created
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I hop from cert to company with care,

Ensuring keys awake in every lair.
One credential set, each job a queue,
I nudge AuthWorker to see it through.
Hooray—secure connections, stitched anew! 🥕✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: auto-creating credentials for all of a user's companies when uploading a certificate, which is the primary objective.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/auto-create-credentials-for-all-companies

Comment @coderabbitai help to get the list of available commands and usage tips.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lib/ksef_hub_web/live/certificate_live.ex (1)

183-185: ⚠️ Potential issue | 🔴 Critical

Fix unused variable company — build is failing.

The pipeline failure indicates company is assigned but never used. Since the credential/auth logic now iterates over all user's companies via ensure_credentials_and_auth_for_user(user), this variable is dead code.

🐛 Proposed fix
   defp save_certificate(socket, cert_data, password) do
     user = socket.assigns.current_user
-    company = socket.assigns.current_company
     cert_meta = extract_certificate_info(cert_data, password)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/ksef_hub_web/live/certificate_live.ex` around lines 183 - 185, The local
variable company in save_certificate (assigned from
socket.assigns.current_company) is unused now and causing the build failure;
remove the dead assignment or rename it to _company to silence the warning,
leaving the rest of save_certificate intact, since credential/auth flow now uses
ensure_credentials_and_auth_for_user(user) and iterates companies elsewhere.
🧹 Nitpick comments (1)
lib/ksef_hub_web/live/company_live/form.ex (1)

228-244: Consider extracting shared enqueue_auth/1 to reduce duplication.

The enqueue_auth/1 function is duplicated in lib/ksef_hub_web/live/certificate_live.ex (lines 276-286). Consider extracting to a shared module (e.g., KsefHub.KsefClient.AuthWorker itself or a helper module) to maintain DRY.

♻️ Example extraction

Add to lib/ksef_hub/ksef_client/auth_worker.ex:

`@spec` enqueue(Ecto.UUID.t()) :: :ok
def enqueue(company_id) do
  case %{company_id: company_id} |> new() |> Oban.insert() do
    {:ok, _job} -> :ok
    {:error, reason} ->
      Logger.error("Failed to enqueue auth job for company #{company_id}: #{inspect(reason)}")
      :ok
  end
end

Then both LiveViews can call AuthWorker.enqueue(company_id).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/ksef_hub_web/live/company_live/form.ex` around lines 228 - 244, The
duplicate enqueue_auth/1 logic should be moved into a shared function on the
AuthWorker module and both LiveViews should call that instead; create
KsefHub.KsefClient.AuthWorker.enqueue/1 which accepts company_id, builds the job
with AuthWorker.new(company_id) (or new()), inserts via Oban.insert(), and logs
failures with the inspected reason, returning :ok; then replace the local
enqueue_auth/1 definitions in CompanyLive.Form and CertificateLive with calls to
AuthWorker.enqueue(company_id).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@lib/ksef_hub_web/live/certificate_live.ex`:
- Around line 183-185: The local variable company in save_certificate (assigned
from socket.assigns.current_company) is unused now and causing the build
failure; remove the dead assignment or rename it to _company to silence the
warning, leaving the rest of save_certificate intact, since credential/auth flow
now uses ensure_credentials_and_auth_for_user(user) and iterates companies
elsewhere.

---

Nitpick comments:
In `@lib/ksef_hub_web/live/company_live/form.ex`:
- Around line 228-244: The duplicate enqueue_auth/1 logic should be moved into a
shared function on the AuthWorker module and both LiveViews should call that
instead; create KsefHub.KsefClient.AuthWorker.enqueue/1 which accepts
company_id, builds the job with AuthWorker.new(company_id) (or new()), inserts
via Oban.insert(), and logs failures with the inspected reason, returning :ok;
then replace the local enqueue_auth/1 definitions in CompanyLive.Form and
CertificateLive with calls to AuthWorker.enqueue(company_id).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b05d96eb-62be-4479-a8da-1e24049dc4e6

📥 Commits

Reviewing files that changed from the base of the PR and between 18a9ac7 and 3d5b53d.

📒 Files selected for processing (6)
  • lib/ksef_hub/credentials.ex
  • lib/ksef_hub_web/live/certificate_live.ex
  • lib/ksef_hub_web/live/company_live/form.ex
  • test/ksef_hub/credentials_test.exs
  • test/ksef_hub_web/live/certificate_live_test.exs
  • test/ksef_hub_web/live/company_live_test.exs

Move duplicate enqueue_auth/1 from CertificateLive and CompanyLive.Form
into AuthWorker.enqueue/1 so both callers share one implementation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/ksef_hub_web/live/certificate_live.ex`:
- Around line 216-229: The current ensure_credentials_and_auth_for_user/1 calls
Companies.list_companies_for_user/1 which returns all memberships; if you only
want to create credentials for companies the user owns, change the call to use a
owners-only query (e.g. add a membership role filter like where(... and m.role
== :owner) inside Companies.list_companies_for_user/1 or create and call a new
Companies.list_owned_companies_for_user/1), then keep the existing
Credentials.ensure_credential_for_company/1 handling (enqueue_auth on {:ok, _}
and Logger.warning on {:error, _}) so only owner companies are processed; if
instead the current behavior is intended, add an explicit comment in
ensure_credentials_and_auth_for_user/1 to document that it runs for all active
memberships.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 747918c6-edd1-4f0e-9f5b-24f1105509ad

📥 Commits

Reviewing files that changed from the base of the PR and between 3d5b53d and 734e44e.

📒 Files selected for processing (1)
  • lib/ksef_hub_web/live/certificate_live.ex

Comment thread lib/ksef_hub_web/live/certificate_live.ex
list_companies_for_user returns all companies including where user is
accountant. Only owners should have credentials created on cert upload.

Adds Companies.list_owned_companies_for_user/1 and uses it in
ensure_credentials_and_auth_for_user/1.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/ksef_hub/ksef_client/auth_worker.ex`:
- Around line 16-25: The enqueue/1 function currently logs Oban.insert failures
but still returns :ok, masking errors; change enqueue/1 (and its `@spec`) to
propagate the insertion result instead of always returning :ok: return {:ok,
job} on {:ok, job} and propagate or return {:error, reason} on {:error, reason}
from Oban.insert (keeping the Logger.error call if desired), so callers can
detect and handle enqueue failures; locate the logic in enqueue/1 that calls
new() |> Oban.insert() and update the pattern matches and spec accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 536888ec-ffed-491b-b397-63cc0cec31c0

📥 Commits

Reviewing files that changed from the base of the PR and between 734e44e and 021c4c0.

📒 Files selected for processing (4)
  • lib/ksef_hub/companies.ex
  • lib/ksef_hub/ksef_client/auth_worker.ex
  • lib/ksef_hub_web/live/certificate_live.ex
  • lib/ksef_hub_web/live/company_live/form.ex
🚧 Files skipped from review as they are similar to previous changes (2)
  • lib/ksef_hub_web/live/company_live/form.ex
  • lib/ksef_hub_web/live/certificate_live.ex

Comment on lines +16 to +25
@spec enqueue(Ecto.UUID.t()) :: :ok
def enqueue(company_id) do
case %{company_id: company_id} |> new() |> Oban.insert() do
{:ok, _job} ->
:ok

{:error, reason} ->
Logger.error("Failed to enqueue auth job for company #{company_id}: #{inspect(reason)}")
:ok
end

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Do not mask enqueue failures as success

enqueue/1 always returns :ok; on Line 22-Line 24, insertion failures are logged but still reported as success. This makes auth bootstrap failures non-actionable for callers and can silently leave a company without an auth job.

Suggested fix
-  `@doc` "Enqueues an auth job for the given company. Returns `:ok` always."
-  `@spec` enqueue(Ecto.UUID.t()) :: :ok
+  `@doc` "Enqueues an auth job for the given company."
+  `@spec` enqueue(Ecto.UUID.t()) :: :ok | {:error, term()}
   def enqueue(company_id) do
     case %{company_id: company_id} |> new() |> Oban.insert() do
       {:ok, _job} ->
         :ok

       {:error, reason} ->
         Logger.error("Failed to enqueue auth job for company #{company_id}: #{inspect(reason)}")
-        :ok
+        {:error, reason}
     end
   end
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/ksef_hub/ksef_client/auth_worker.ex` around lines 16 - 25, The enqueue/1
function currently logs Oban.insert failures but still returns :ok, masking
errors; change enqueue/1 (and its `@spec`) to propagate the insertion result
instead of always returning :ok: return {:ok, job} on {:ok, job} and propagate
or return {:error, reason} on {:error, reason} from Oban.insert (keeping the
Logger.error call if desired), so callers can detect and handle enqueue
failures; locate the logic in enqueue/1 that calls new() |> Oban.insert() and
update the pattern matches and spec accordingly.

@emilwojtaszek emilwojtaszek merged commit 1d339c3 into main Apr 1, 2026
3 checks passed
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.

1 participant