-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Create clawback #2525
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create clawback #2525
Conversation
|
The latest updates on your projects. Learn more about Vercel for Git βοΈ
|
WalkthroughThis change introduces support for "clawbacks"βnegative commissionsβacross the commissions and payouts system. It adds UI components for creating clawbacks, updates backend logic and schemas to handle negative earnings, and enhances tables to display reasons for clawbacks with tooltips. Filtering and aggregation logic is broadened to include both positive and negative (non-zero) earnings. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant UI (Dashboard)
participant CreateClawbackSheet
participant Backend (createClawbackAction)
participant DB
User->>UI (Dashboard): Click "Create clawback"
UI (Dashboard)->>CreateClawbackSheet: Open sheet/modal
User->>CreateClawbackSheet: Fill partner, amount, reason, submit
CreateClawbackSheet->>Backend (createClawbackAction): Submit validated data
Backend (createClawbackAction)->>DB: Create negative commission (clawback)
DB-->>Backend (createClawbackAction): Success/Failure
Backend (createClawbackAction)-->>CreateClawbackSheet: Response
CreateClawbackSheet-->>UI (Dashboard): Show toast, refresh commissions
sequenceDiagram
participant Cron
participant Backend (create-payout)
participant DB
Cron->>Backend (create-payout): Trigger payout creation
Backend (create-payout)->>DB: Query positive commissions (earnings > 0)
Backend (create-payout)->>DB: Query negative commissions (earnings < 0)
DB-->>Backend (create-payout): Return commissions and clawbacks
Backend (create-payout)->>DB: Create payout, update all commissions/clawbacks
DB-->>Backend (create-payout): Success
Possibly related PRs
Poem
π Recent review detailsConfiguration used: CodeRabbit UI π Files selected for processing (1)
π§ Files skipped from review as they are similar to previous changes (1)
β° Context from checks skipped due to timeout of 90000ms (1)
β¨ Finishing Touches
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. πͺ§ TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
|
π€ Bug0 QA Agent Here are the results of the automated tests for PR #2525:
To re-run the tests, please comment |
|
π€ Bug0 QA Agent Here are the results of the automated tests for PR #2525:
To re-run the tests, please comment |
|
π€ Bug0 QA Agent Here are the results of the automated tests for PR #2525:
To re-run the tests, please comment |
|
π€ Bug0 QA Agent Here are the results of the automated tests for PR #2525:
To re-run the tests, please comment |
|
π€ Bug0 QA Agent Here are the results of the automated tests for PR #2525:
To re-run the tests, please comment |
|
π€ Bug0 QA Agent Here are the results of the automated tests for PR #2525:
To re-run the tests, please comment |
β¦ filtering logic. Introduce CLAWBACK_REASONS_MAP for better reason handling in earnings display.
|
π€ Bug0 QA Agent Here are the results of the automated tests for PR #2525:
To re-run the tests, please comment |
|
π€ Bug0 QA Agent Here are the results of the automated tests for PR #2525:
To re-run the tests, please comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
π Outside diff range comments (1)
apps/web/lib/partners/create-partner-commission.ts (1)
28-45:descriptionparam lacks basic sanitation / length guardA free-text
descriptionis now forwarded straight to the DB.
Consider trimming and guarding length (e.g. 255 chars) at the boundary (here or in the zod schema) to prevent oversized payloads and UI truncation issues.- description?: string; + /** Optional, max 255 chars, trimmed */ + description?: string;A simple
description?.trim().slice(0, 255)before thecreatewould suffice.
β»οΈ Duplicate comments (3)
apps/web/app/(ee)/api/partner-profile/programs/[programId]/earnings/route.ts (1)
44-46: See previous comment about extracting a sharedNON_ZERO_EARNINGS_FILTER.apps/web/app/(ee)/api/partner-profile/programs/[programId]/earnings/count/route.ts (1)
36-38: See previous comment about extracting a sharedNON_ZERO_EARNINGS_FILTER.apps/web/app/(ee)/api/commissions/count/route.ts (1)
33-35: See previous comment about extracting a sharedNON_ZERO_EARNINGS_FILTER.
π§Ή Nitpick comments (6)
apps/web/app/(ee)/api/commissions/route.ts (1)
45-47: Consider centralising the βnon-zero earningsβ filterThe new predicate
{ earnings: { not: 0 } }appears in several endpoints in this PR. Duplicating the literal across files risks future inconsistency. Extracting a small helper (e.g.NON_ZERO_EARNINGS_FILTER) in a shared module or util would keep these routes DRY and makes later changes trivial.-earnings: { not: 0 }, +...NON_ZERO_EARNINGS_FILTER,apps/web/lib/partners/create-partner-commission.ts (1)
180-182: Persisting empty strings
descriptionis inserted even when it is an empty string, creating noisy data.
Either storenullor omit the field when empty:- description, + ...(description?.trim() + ? { description: description.trim() } + : {}),apps/web/app/(ee)/partners.dub.co/(dashboard)/programs/[programSlug]/(enrolled)/earnings/earnings-table.tsx (2)
6-6: Import order nit
CLAWBACK_REASONS_MAPis a domain constant; group it with other absolute imports to keep the block ordered & avoid merge churn.
166-204: Tooltip rendering logic duplicated across tablesThe earnings-cell logic (tooltip, red negative, underline) is almost identical to the implementation in the commissions table. Extract into a small
<EarningsCell />component to avoid divergence and keep styles in sync.No functional issues, just maintainability.
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/commission-popover-buttons.tsx (1)
41-46: Add an accessible label to the icon-only triggerScreen-reader users receive no context for the three-dot button.
- <Button + <Button + aria-label="More commission actions"apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/commission-table.tsx (1)
167-178: Long tooltip strings may overflow the viewport.Some
CLAWBACK_REASONS_MAPdescriptions are lengthy; without width constraints, the tooltip can extend past the screen on narrow layouts.
Consider settingmax-w-xs(or similar) on the tooltip content.
π Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
π Files selected for processing (15)
apps/web/app/(ee)/api/commissions/count/route.ts(1 hunks)apps/web/app/(ee)/api/commissions/route.ts(1 hunks)apps/web/app/(ee)/api/cron/payouts/create-payout.ts(4 hunks)apps/web/app/(ee)/api/partner-profile/programs/[programId]/earnings/count/route.ts(1 hunks)apps/web/app/(ee)/api/partner-profile/programs/[programId]/earnings/route.ts(1 hunks)apps/web/app/(ee)/partners.dub.co/(dashboard)/programs/[programSlug]/(enrolled)/earnings/earnings-table.tsx(3 hunks)apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/commission-popover-buttons.tsx(1 hunks)apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/commission-table.tsx(4 hunks)apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/create-clawback-sheet.tsx(1 hunks)apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/page.tsx(1 hunks)apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/import-export-buttons.tsx(1 hunks)apps/web/lib/actions/partners/create-clawback.ts(1 hunks)apps/web/lib/api/partners/sync-total-commissions.ts(1 hunks)apps/web/lib/partners/create-partner-commission.ts(3 hunks)apps/web/lib/zod/schemas/commissions.ts(2 hunks)
π§° Additional context used
𧬠Code Graph Analysis (3)
apps/web/lib/actions/partners/create-clawback.ts (5)
apps/web/lib/actions/safe-action.ts (1)
authActionClient(33-81)apps/web/lib/zod/schemas/commissions.ts (1)
createClawbackSchema(186-193)apps/web/lib/api/programs/get-default-program-id-or-throw.ts (1)
getDefaultProgramIdOrThrow(4-17)apps/web/lib/api/programs/get-program-enrollment-or-throw.ts (1)
getProgramEnrollmentOrThrow(6-80)apps/web/lib/partners/create-partner-commission.ts (1)
createPartnerCommission(16-204)
apps/web/app/(ee)/partners.dub.co/(dashboard)/programs/[programSlug]/(enrolled)/earnings/earnings-table.tsx (2)
apps/web/lib/zod/schemas/commissions.ts (1)
CLAWBACK_REASONS_MAP(182-184)packages/ui/src/tooltip.tsx (1)
Tooltip(32-88)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/commission-popover-buttons.tsx (1)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/create-clawback-sheet.tsx (1)
useCreateClawbackSheet(242-256)
π Additional comments (4)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/import-export-buttons.tsx (1)
13-14: Null-safety forslug
useWorkspace()now only destructures{ slug }. Ifslugcan ever beundefined(e.g. initial loading state or error), pushingrouter.push(\/${slug}/β¦`)will generate a URL with"undefined"` and break navigation. Consider guarding or early-returning:-router.push(`/${slug}/program/partners?import=rewardful`); +if (!slug) return; // or show error / spinner +router.push(`/${slug}/program/partners?import=rewardful`);apps/web/lib/api/partners/sync-total-commissions.ts (1)
14-18: Possible over-count ifearningscan be NULL
earnings: { not: 0 }includes both negative & positive numbers, but also matchesNULLvalues (NULL != 0).
If older rows or future migrations ever leaveearningsnullable, those rows will be summed as0, silently inflating totals.- earnings: { not: 0 }, + // Exclude zero *and* NULL from the aggregation + earnings: { notIn: [0, null] },Double-check the Prisma schema and existing data; if the column is defined as
NOT NULLthe fix is optional, otherwise apply the safeguard.apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/page.tsx (1)
12-20: π Nice contextual grouping of buttonsAdding
CommissionPopoverButtonsnext toCreateCommissionButtonkeeps the primary action visible while hiding secondary actions behind a popover. Looks good.apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/create-clawback-sheet.tsx (1)
75-76: Keep client & server validation consistent.The submit button disables zero amounts (
!amount), but the Zod schema still allows it. Once the schema is tightened (see earlier comment), this divergence disappears; otherwise users could bypass the UI and call the action directly withamount = 0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
π§Ή Nitpick comments (2)
apps/web/app/(ee)/api/cron/payouts/create-payout.ts (2)
55-60:commonWherecould be hoisted or memoised
commonWhereis re-allocated on every cron run. Moving it outside the function (or at least marking itconst+ reusing between the two queries) would shave a tiny amount off GC pressure and make the Promise.all block a bit cleaner.
107-118: Logging is asymmetricThe positive-commission log is still hard-coded to βpending commissionsβ while the second log says βpending clawbacksβ.
Unify the wording or group them in a single structured log entry to avoid confusion in downstream log analysis.
π Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
π Files selected for processing (2)
apps/web/app/(ee)/api/cron/payouts/create-payout.ts(4 hunks)apps/web/lib/zod/schemas/commissions.ts(2 hunks)
π§ Files skipped from review as they are similar to previous changes (1)
- apps/web/lib/zod/schemas/commissions.ts
β° Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: build
π Additional comments (3)
apps/web/app/(ee)/api/cron/payouts/create-payout.ts (3)
119-123: Period boundary bug is fixed β good catch!Explicitly sorting the concatenated array before deriving
periodStart/periodEndremoves the ordering assumption flagged in the previous review. β
190-204: Month-spanning payouts β verify business intent
periodEndis alwaysendOfMonth(latestCommissionDate). IfallCommissionsspans multiple months (e.g. July 25 β Aug 03) the payout will lump both months into an August period.
Confirm this matches financeβs expectations; otherwise compute separate payouts per month or capperiodEndat the end of the earliest month.
135-160: ```shell
#!/bin/bash
set -eecho "Locating Prisma schema files..."
fd schema.prismaecho -e "\nInspecting Payout model in schema..."
rg "model Payout" -A20echo -e "\nChecking for composite @@unique constraints..."
rg "@@unique" -A5</details> </blockquote></details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
|
π€ Bug0 QA Agent Here are the results of the automated tests for PR #2525:
To re-run the tests, please comment |
|
π€ Bug0 QA Agent Here are the results of the automated tests for PR #2525:
To re-run the tests, please comment |
|
π€ Bug0 QA Agent Here are the results of the automated tests for PR #2525:
To re-run the tests, please comment |
|
π€ Bug0 QA Agent Here are the results of the automated tests for PR #2525:
To re-run the tests, please comment |
|
@CodeRabbit final review |
|
Tip For best results, initiate chat on the files or code changes. π§© Analysis chainπ Script executed: Length of output: 10240 π Script executed: Length of output: 9309 π Script executed: Length of output: 6862 π Script executed: Length of output: 7853 @steven-tey Excellent work on this clawback implementation! π After conducting a comprehensive final review, I'm impressed with the thoroughness and quality of this feature. Here's my assessment: β Strengths & Implementation Qualityπ Security & Validation
ποΈ Architecture & Data Consistency
π» User Experience
π§ Edge Case Handling
π― Key Implementation Highlights// Smart payout logic separating commissions and clawbacks
const [commissions, clawbacks] = await Promise.all([
// Commissions wait for holding period
prisma.commission.findMany({
where: { earnings: { gt: 0 }, ...commonWhere, ...holdingPeriodFilter },
}),
// Clawbacks processed immediately
prisma.commission.findMany({
where: { earnings: { lt: 0 }, ...commonWhere },
}),
]);// Excellent UI feedback with tooltips
if (commission.description) {
const reason = CLAWBACK_REASONS_MAP[commission.description]?.description;
return (
<Tooltip content={reason}>
<span className={cn("underline decoration-dotted",
commission.earnings < 0 && "text-red-600")}>
{earnings}
</span>
</Tooltip>
);
}π Ready for ProductionThe implementation demonstrates:
This clawback feature is production-ready and follows excellent engineering practices. The integration between positive commissions and negative clawbacks is seamless, and the user interface provides clear visual feedback for different types of earnings. Outstanding work on delivering a feature that's both technically sound and user-friendly! π |
Summary by CodeRabbit
New Features
Enhancements
Bug Fixes
Chores