Skip to content

feat(taxcode): prevent deletion of add-on-referenced tax codes#4561

Draft
borbelyr-kong wants to merge 3 commits into
feat/taxcode-plan-publish-validationfrom
feat/taxcode-addon-delete-guard
Draft

feat(taxcode): prevent deletion of add-on-referenced tax codes#4561
borbelyr-kong wants to merge 3 commits into
feat/taxcode-plan-publish-validationfrom
feat/taxcode-addon-delete-guard

Conversation

@borbelyr-kong

Copy link
Copy Markdown
Contributor

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e2a844e9-2062-4fd4-9b43-f8334c1ace98

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/taxcode-addon-delete-guard

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@borbelyr-kong

Copy link
Copy Markdown
Contributor Author

@greptileai review this draft

@greptile-apps

greptile-apps Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR introduces a pre-delete guard that prevents a tax code from being deleted while it is still referenced by add-on rate cards. It follows the same hook pattern as the existing plan-validator hook.

  • Adds a TaxCodes filter to ListAddonsInput and the Ent adapter query, joining through addon_ratecards with a deleted_at IS NULL guard so soft-deleted rate cards are excluded.
  • Wires the new AddonHook into the tax code service via the existing RegisterHooks mechanism; both unit tests (stub service) and integration tests (Postgres) are included.

Confidence Score: 4/5

The change is safe to merge; the core guard logic correctly blocks deletion whenever any add-on holds a live rate-card reference to the tax code.

The hook and adapter filter are well-structured and closely mirror the proven plan-validator hook. The only gap is that add-ons in Invalid status (effectiveTo < effectiveFrom) are not checked, so a tax code could be deleted while a broken add-on still references it, preventing that add-on from ever being repaired. Both unit and integration tests cover the happy and blocking paths.

openmeter/taxcode/service/hooks/addonhook.go — the status list in PreDelete warrants a second look

Important Files Changed

Filename Overview
openmeter/taxcode/service/hooks/addonhook.go New PreDelete hook that queries add-ons by tax code ID before allowing deletion; mirrors the plan validator hook but omits AddonStatusInvalid from the status filter
openmeter/productcatalog/addon/adapter/addon.go Adds TaxCodes filter to ListAddons — uses HasRatecardsWith with DeletedAtIsNil() predicate, correctly scoping to non-deleted rate cards
openmeter/taxcode/errors.go Adds ErrCodeTaxCodeReferencedByAddon error type following the same pattern as the existing plan error
app/common/taxcode.go Registers the new addon hook on the tax code service via wire; follows the same pattern as TaxCodePlanValidatorHook
openmeter/taxcode/service/hooks/addonhook_test.go Unit tests for the addon hook with a stub addon service; covers block and allow paths
openmeter/productcatalog/addon/adapter/adapter_test.go Integration tests for the new TaxCodes filter on ListAddons; covers filter-by-A, filter-by-B, and nonexistent tax code cases
openmeter/productcatalog/addon/service.go Adds TaxCodes field to ListAddonsInput with validation, consistent with other filter fields
cmd/server/wire_gen.go Auto-generated wire code instantiating the addon hook after both addon and taxcode services are available

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Client
    participant TaxCodeService
    participant AddonHook
    participant AddonService
    participant DB

    Client->>TaxCodeService: DeleteTaxCode(id)
    TaxCodeService->>AddonHook: PreDelete(taxCode)
    AddonHook->>AddonService: "ListAddons(namespace, status=[Active,Draft,Archived], taxCodes=[id], page=5)"
    AddonService->>DB: "SELECT addons WHERE has_ratecards(tax_code_id=id, deleted_at IS NULL)"
    DB-->>AddonService: []Addon
    AddonService-->>AddonHook: "Result{Items, TotalCount}"
    alt "len(Items) > 0"
        AddonHook-->>TaxCodeService: ErrTaxCodeReferencedByAddon(taxCodeID, addonIDs)
        TaxCodeService-->>Client: 409 Conflict
    else "len(Items) == 0"
        AddonHook-->>TaxCodeService: nil
        TaxCodeService->>DB: "DELETE tax_code WHERE id=id"
        TaxCodeService-->>Client: 200 OK
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Client
    participant TaxCodeService
    participant AddonHook
    participant AddonService
    participant DB

    Client->>TaxCodeService: DeleteTaxCode(id)
    TaxCodeService->>AddonHook: PreDelete(taxCode)
    AddonHook->>AddonService: "ListAddons(namespace, status=[Active,Draft,Archived], taxCodes=[id], page=5)"
    AddonService->>DB: "SELECT addons WHERE has_ratecards(tax_code_id=id, deleted_at IS NULL)"
    DB-->>AddonService: []Addon
    AddonService-->>AddonHook: "Result{Items, TotalCount}"
    alt "len(Items) > 0"
        AddonHook-->>TaxCodeService: ErrTaxCodeReferencedByAddon(taxCodeID, addonIDs)
        TaxCodeService-->>Client: 409 Conflict
    else "len(Items) == 0"
        AddonHook-->>TaxCodeService: nil
        TaxCodeService->>DB: "DELETE tax_code WHERE id=id"
        TaxCodeService-->>Client: 200 OK
    end
Loading

Fix All in Claude Code

Reviews (1): Last reviewed commit: "feat(taxcode): prevent deletion of add-o..." | Re-trigger Greptile

Comment on lines +53 to +59
affectedAddons, err := e.addonService.ListAddons(ctx, addon.ListAddonsInput{
Namespaces: []string{tc.Namespace},
Status: []productcatalog.AddonStatus{
productcatalog.AddonStatusActive,
productcatalog.AddonStatusDraft,
productcatalog.AddonStatusArchived,
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 The PreDelete check omits AddonStatusInvalid, unlike the plan validator hook which covers all plan statuses. An add-on enters Invalid state when effectiveTo < effectiveFrom (an already-inconsistent record), but it still holds a tax_code_id FK on its rate cards. Allowing the tax code to be deleted while such an add-on exists would deepen the inconsistency and make the add-on unrecoverable if someone later tries to fix its time range.

Suggested change
affectedAddons, err := e.addonService.ListAddons(ctx, addon.ListAddonsInput{
Namespaces: []string{tc.Namespace},
Status: []productcatalog.AddonStatus{
productcatalog.AddonStatusActive,
productcatalog.AddonStatusDraft,
productcatalog.AddonStatusArchived,
},
affectedAddons, err := e.addonService.ListAddons(ctx, addon.ListAddonsInput{
Namespaces: []string{tc.Namespace},
Status: []productcatalog.AddonStatus{
productcatalog.AddonStatusActive,
productcatalog.AddonStatusDraft,
productcatalog.AddonStatusArchived,
productcatalog.AddonStatusInvalid,
},

Fix in Claude Code

@borbelyr-kong borbelyr-kong force-pushed the feat/taxcode-plan-publish-validation branch from 8a31078 to 7a906df Compare June 23, 2026 21:01
@borbelyr-kong borbelyr-kong force-pushed the feat/taxcode-addon-delete-guard branch from e850ada to 5946f52 Compare June 23, 2026 21:01
@borbelyr-kong borbelyr-kong force-pushed the feat/taxcode-plan-publish-validation branch 2 times, most recently from 2f9c5ab to 3a58a03 Compare June 24, 2026 13:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant