diff --git a/server/src/internal/customers/attach/attachFunctions/upgradeSameIntFlow/handleUpgradeSameInt.ts b/server/src/internal/customers/attach/attachFunctions/upgradeSameIntFlow/handleUpgradeSameInt.ts
index 9a9b3cb18..188676d9e 100644
--- a/server/src/internal/customers/attach/attachFunctions/upgradeSameIntFlow/handleUpgradeSameInt.ts
+++ b/server/src/internal/customers/attach/attachFunctions/upgradeSameIntFlow/handleUpgradeSameInt.ts
@@ -67,7 +67,10 @@ export const handleUpgradeSameInterval = async ({
disableFreeTrial: config.disableTrial,
carryExistingUsages: config.carryUsage,
carryOverTrial: config.carryTrial,
- anchorToUnix: stripeSubs[0].current_period_end * 1000,
+ anchorToUnix:
+ stripeSubs.length > 0
+ ? stripeSubs[0].current_period_end * 1000
+ : undefined,
logger,
});
diff --git a/server/src/internal/customers/attach/handleAttachPreview/getUpgradeProductPreview.ts b/server/src/internal/customers/attach/handleAttachPreview/getUpgradeProductPreview.ts
index 6f82f5948..59954f423 100644
--- a/server/src/internal/customers/attach/handleAttachPreview/getUpgradeProductPreview.ts
+++ b/server/src/internal/customers/attach/handleAttachPreview/getUpgradeProductPreview.ts
@@ -26,6 +26,7 @@ import {
import { freeTrialToStripeTimestamp } from "@/internal/products/free-trials/freeTrialUtils.js";
import { Decimal } from "decimal.js";
import { intervalsAreSame } from "../attachUtils/getAttachConfig.js";
+import { isFreeProduct } from "@/internal/products/productUtils.js";
const getNextCycleAt = ({
prices,
@@ -107,9 +108,10 @@ export const getUpgradeProductPreview = async ({
// Get prorated amounts for new product
const newProduct = attachParamsToProduct({ attachParams });
const intervalsSame = intervalsAreSame({ attachParams });
- const anchorToUnix = intervalsSame
- ? stripeSubs[0].current_period_end * 1000
- : undefined;
+ const anchorToUnix =
+ intervalsSame && stripeSubs.length > 0
+ ? stripeSubs[0].current_period_end * 1000
+ : undefined;
const newPreviewItems = await getItemsForNewProduct({
newProduct,
@@ -122,21 +124,30 @@ export const getUpgradeProductPreview = async ({
});
const lastInterval = getLastInterval({ prices: newProduct.prices });
- const nextCycleAt = getNextCycleAt({
- prices: newProduct.prices,
- stripeSubs,
- willCycleReset: !intervalsSame,
- interval: lastInterval,
- now,
- freeTrial: attachParams.freeTrial,
- });
- let nextCycleItems = await getItemsForNewProduct({
- newProduct,
- attachParams,
- interval: attachParams.freeTrial ? undefined : lastInterval,
- logger,
- });
+ let dueNextCycle = undefined;
+ if (!isFreeProduct(newProduct.prices)) {
+ const nextCycleAt = getNextCycleAt({
+ prices: newProduct.prices,
+ stripeSubs,
+ willCycleReset: !intervalsSame,
+ interval: lastInterval,
+ now,
+ freeTrial: attachParams.freeTrial,
+ });
+
+ let nextCycleItems = await getItemsForNewProduct({
+ newProduct,
+ attachParams,
+ interval: attachParams.freeTrial ? undefined : lastInterval,
+ logger,
+ });
+
+ dueNextCycle = {
+ line_items: nextCycleItems,
+ due_at: nextCycleAt.next_cycle_at,
+ };
+ }
let items = [...curPreviewItems, ...newPreviewItems];
@@ -175,7 +186,7 @@ export const getUpgradeProductPreview = async ({
if (branch == AttachBranch.UpdatePrepaidQuantity) {
items = items.filter((item) => item.usage_model == UsageModel.Prepaid);
- nextCycleItems = nextCycleItems.filter(
+ dueNextCycle!.line_items = dueNextCycle!.line_items.filter(
(item) => item.usage_model == UsageModel.Prepaid,
);
}
@@ -197,10 +208,7 @@ export const getUpgradeProductPreview = async ({
return {
currency: attachParams.org.default_currency,
due_today: dueToday,
- due_next_cycle: {
- line_items: nextCycleItems,
- due_at: nextCycleAt.next_cycle_at,
- },
+ due_next_cycle: dueNextCycle,
options,
};
diff --git a/server/src/internal/customers/handlers/handleUpdateEntitlement.ts b/server/src/internal/customers/handlers/handleUpdateEntitlement.ts
index 55b8ce169..c34c5a742 100644
--- a/server/src/internal/customers/handlers/handleUpdateEntitlement.ts
+++ b/server/src/internal/customers/handlers/handleUpdateEntitlement.ts
@@ -2,11 +2,7 @@ import { handleRequestError } from "@/utils/errorUtils.js";
import { CusEntService } from "@/internal/customers/cusProducts/cusEnts/CusEntitlementService.js";
import RecaseError from "@/utils/errorUtils.js";
-import {
- ErrCode,
- FullCustomerEntitlement,
- FullCusEntWithProduct,
-} from "@autumn/shared";
+import { ErrCode, FullCustomerEntitlement } from "@autumn/shared";
import { Decimal } from "decimal.js";
import { StatusCodes } from "http-status-codes";
import { adjustAllowance } from "@/trigger/adjustAllowance.js";
diff --git a/server/src/internal/products/product-items/compareItemUtils.ts b/server/src/internal/products/product-items/compareItemUtils.ts
index d9a372a78..ec66b0f6f 100644
--- a/server/src/internal/products/product-items/compareItemUtils.ts
+++ b/server/src/internal/products/product-items/compareItemUtils.ts
@@ -173,15 +173,30 @@ export const itemsAreSame = ({
// 1. If feature item
let same = false;
let pricesChanged = false;
+
if (isFeatureItem(item1)) {
+ if (!isFeatureItem(item2)) {
+ return {
+ same: false,
+ pricesChanged: true,
+ };
+ }
+
same = featureItemsAreSame({
item1: FeatureItemSchema.parse(item1),
- item2: FeatureItemSchema.parse(item2),
+ item2: item2 as FeatureItem,
});
pricesChanged = false;
}
if (isFeaturePriceItem(item1)) {
+ if (!isFeaturePriceItem(item2)) {
+ return {
+ same: false,
+ pricesChanged: true,
+ };
+ }
+
const { same: same_, pricesChanged: pricesChanged_ } =
featurePriceItemsAreSame({
item1: FeaturePriceItemSchema.parse(item1),
diff --git a/server/src/internal/products/product-items/mapToItem.ts b/server/src/internal/products/product-items/mapToItem.ts
index fc2772e2c..c787512fb 100644
--- a/server/src/internal/products/product-items/mapToItem.ts
+++ b/server/src/internal/products/product-items/mapToItem.ts
@@ -76,6 +76,7 @@ export const toFeaturePriceItem = ({
ent.feature.config?.usage_type || ProductItemFeatureType.SingleUse,
included_usage: ent.allowance,
+
interval: billingToItemInterval(config.interval!),
price: null,
diff --git a/shared/utils/displayUtils.ts b/shared/utils/displayUtils.ts
index 073960ea6..45b7985bf 100644
--- a/shared/utils/displayUtils.ts
+++ b/shared/utils/displayUtils.ts
@@ -91,7 +91,7 @@ export const getFeatureInvoiceDescription = ({
}) => {
const { singular, plural } = getSingularAndPlural({ feature });
- const usageStr = numberWithCommas(usage);
+ const usageStr = numberWithCommas(Math.ceil(usage));
let result = "";
diff --git a/vite/src/app/layout.tsx b/vite/src/app/layout.tsx
index 209473f08..de0cb15a1 100644
--- a/vite/src/app/layout.tsx
+++ b/vite/src/app/layout.tsx
@@ -21,11 +21,10 @@ import { AutumnProvider } from "autumn-js/react";
import { useAuth } from "@clerk/clerk-react";
export function MainLayout() {
+ const env = useEnv();
const { isLoaded: isUserLoaded, user } = useUser();
const { organization: org } = useOrganization();
- const { setActive } = useOrganizationList();
const { getToken } = useAuth();
- const env = useEnv();
const { pathname } = useLocation();
const navigate = useNavigate();
@@ -85,13 +84,6 @@ export function MainLayout() {
return ;
}
- // // 1. If not org, and memberships > 0, set org active
- // if (!org && user.organizationMemberships.length > 0 && setActive) {
- // setActive({
- // organization: user.organizationMemberships[0].organization.id,
- // });
- // }
-
if (!org && !pathname.includes("/onboarding")) {
return (