diff --git a/backend/src/app.ts b/backend/src/app.ts
index 0b8afa7..6e11a8d 100644
--- a/backend/src/app.ts
+++ b/backend/src/app.ts
@@ -94,6 +94,7 @@ import {
getMembershipForUser,
getNextWorkspaceId,
getWorkspaceById,
+ getWorkspaceBirdCount,
getWorkspaceTotalBirdCount,
listOwnedWorkspacesByOwnerEmail,
listRescueWorkspacesForAdmin,
@@ -951,6 +952,26 @@ const subscriptionAllowsWrite = (workspace: WorkspaceRow) => {
return workspace.subscription_status === 'active' || workspace.subscription_status === 'trialing';
};
+const getBillingPlanBirdLimit = (billingPlan: BillingPlan) => {
+ if (billingPlan === 'rescue_free') {
+ return null;
+ }
+
+ if (billingPlan === 'household_basic') {
+ return 4;
+ }
+
+ if (billingPlan === 'household_plus') {
+ return 10;
+ }
+
+ if (billingPlan === 'household_macaw') {
+ return 16;
+ }
+
+ return null;
+};
+
const mapStripeSubscriptionStatus = (status: Stripe.Subscription.Status): SubscriptionStatus => {
if (status === 'active' || status === 'trialing' || status === 'past_due' || status === 'canceled' || status === 'unpaid') {
return status;
@@ -3118,6 +3139,23 @@ app.post('/api/birds', requireAuth, requireWriteAccess, requireWorkspaceRole(['o
let uploadedObjectKeyToCleanup: string | null = null;
try {
+ const birdLimit = getBillingPlanBirdLimit(req.auth!.workspace.billing_plan);
+
+ if (birdLimit !== null) {
+ const currentBirdCount = await getWorkspaceBirdCount(req.auth!.workspace.id);
+
+ if (currentBirdCount >= birdLimit) {
+ res.status(409).json({
+ error: 'This flock has reached the bird limit for the selected plan. Upgrade the flock subscription or memorialize a bird before adding another.',
+ code: 'billing_plan_bird_limit_reached',
+ birdLimit,
+ currentBirdCount,
+ billingPlan: req.auth!.workspace.billing_plan,
+ });
+ return;
+ }
+ }
+
const birdId = crypto.randomUUID();
const photoStorage = await resolveBirdPhotoStorage({
birdId,
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index ec1a70e..23d5ce5 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -1012,7 +1012,7 @@ const formatBillingPlanCapacity = (billingPlan: BillingPlan) => {
}
if (billingPlan === 'household_plus') {
- return 'Permits 5 to 9 birds in the flock.';
+ return 'Permits 5 to 10 birds in the flock.';
}
if (billingPlan === 'household_macaw') {
@@ -1024,11 +1024,11 @@ const formatBillingPlanCapacity = (billingPlan: BillingPlan) => {
const formatBillingPlanDropdownLabel = (billingPlan: HouseholdBillingPlan) => {
if (billingPlan === 'household_basic') {
- return 'Conure (4 birds)';
+ return 'Conure (up to 4 birds)';
}
if (billingPlan === 'household_plus') {
- return 'Indian Ringneck (5-9 birds)';
+ return 'Indian Ringneck (5-10 birds)';
}
if (billingPlan === 'household_macaw') {
@@ -1066,7 +1066,7 @@ const formatBillingPlanBirdLimit = (billingPlan: BillingPlan) => {
}
if (billingPlan === 'household_plus') {
- return '9';
+ return '10';
}
if (billingPlan === 'household_macaw') {
@@ -6451,8 +6451,8 @@ function App() {
})
}
>
-
-
+
+
@@ -6590,8 +6590,8 @@ function App() {
})
}
>
-
-
+
+