Skip to content

Commit 3175d78

Browse files
Merge branch 'release/2.36.0' into aakash/fix/downgrade-flow-updated
2 parents 4c9b82f + 7e9b763 commit 3175d78

File tree

10 files changed

+418
-78
lines changed

10 files changed

+418
-78
lines changed

src/modules/billing/billing.module.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { DownGradeUserRepository } from "./repositories/downgradeUser.repository
2727
import { DownGradeWorkspaceRepository } from "./repositories/downgradeWorkspace.repository";
2828
import { DownGradeService } from "./services/downgrade.service";
2929
import { ExcelEmailService } from "./services/excel-email.service";
30+
import { UpGradeService } from "./services/upgrade.service";
3031

3132
// Try to import the Stripe module, but don't crash if it's not available
3233
let StripeModule: any;
@@ -57,6 +58,7 @@ export class BillingModule {
5758
DownGradeUserRepository,
5859
DownGradeWorkspaceRepository,
5960
DownGradeService,
61+
UpGradeService,
6062
ExcelEmailService,
6163
BillingAuditService,
6264
PromoCodeService,
@@ -87,6 +89,7 @@ export class BillingModule {
8789
DownGradeUserRepository,
8890
DownGradeWorkspaceRepository,
8991
DownGradeService,
92+
UpGradeService,
9093
ExcelEmailService,
9194
PricingService,
9295
PaymentEmailService,

src/modules/billing/controllers/stripe.controller.ts

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import { ConfigService } from "@nestjs/config";
5050
import { TrialType } from "@src/modules/common/enum/trial.enum";
5151
import { PricingPlan } from "@src/modules/common/models/pricing.model";
5252
import { DownGradeService } from "../services/downgrade.service";
53+
import { UpGradeService } from "../services/upgrade.service";
5354

5455
// Dynamically import Stripe services
5556
let StripeService: any;
@@ -74,6 +75,7 @@ export class StripeController {
7475
private readonly configService: ConfigService,
7576
private readonly salesEmailRepository: SalesEmailRepository,
7677
private readonly downgradeService: DownGradeService,
78+
private readonly upgradeService: UpGradeService,
7779
) {
7880
this.isStripeAvailable = !!this.stripeService;
7981

@@ -342,6 +344,15 @@ export class StripeController {
342344
}
343345
}
344346
}
347+
if (
348+
createSubscriptionDto?.isUpgrade &&
349+
createSubscriptionDto?.workspaces
350+
) {
351+
await this.upgradeService.addUpgradeDetails(
352+
createSubscriptionDto?.metadata?.hubId,
353+
createSubscriptionDto?.workspaces,
354+
);
355+
}
345356

346357
return subscription;
347358
} catch (error) {
@@ -425,13 +436,27 @@ export class StripeController {
425436
updateSubscriptionDto.seats,
426437
updateSubscriptionDto.paymentBehavior,
427438
);
428-
if (subscription && updateSubscriptionDto?.workspaces) {
439+
if (
440+
subscription &&
441+
updateSubscriptionDto?.workspaces &&
442+
!updateSubscriptionDto?.isUpgrade
443+
) {
429444
await this.downgradeService.addDowgradeDetails(
430445
updateSubscriptionDto?.metadata?.hubId,
431446
updateSubscriptionDto?.workspaces,
432447
updateSubscriptionDto?.users,
433448
);
434449
}
450+
if (
451+
subscription &&
452+
updateSubscriptionDto?.isUpgrade &&
453+
updateSubscriptionDto?.workspaces
454+
) {
455+
await this.upgradeService.addUpgradeDetails(
456+
updateSubscriptionDto?.metadata?.hubId,
457+
updateSubscriptionDto?.workspaces,
458+
);
459+
}
435460

436461
return subscription;
437462
} catch (error) {
@@ -776,4 +801,49 @@ export class StripeController {
776801
);
777802
}
778803
}
804+
805+
@UseGuards(JwtAuthGuard, RolesGuard)
806+
@Roles("user", "admin")
807+
@Get("/:teamId/shared-workspaces/:selectedPlan")
808+
@ApiOperation({
809+
summary: "Check and unrestrict workspaces if required",
810+
description:
811+
"Checks whether workspaces can be unrestricted/restored based on the selected plan for a team.",
812+
})
813+
@ApiParam({
814+
name: "teamId",
815+
description: "Team ID",
816+
example: "675a84b1bd31d451443ae019",
817+
})
818+
@ApiParam({
819+
name: "selectedPlan",
820+
description: "Plan selected for upgrade",
821+
example: "professional_monthly",
822+
})
823+
@ApiResponse({
824+
status: 200,
825+
description: "unrestricted workspaces returned successfully",
826+
})
827+
@ApiResponse({ status: 404, description: "Team or data not found" })
828+
@ApiResponse({
829+
status: 400,
830+
description: "Cannot get unrestrict workspaces.",
831+
})
832+
async getUnRestrictWorkspaces(
833+
@Param("teamId") teamId: string,
834+
@Param("selectedPlan") selectedPlan: string,
835+
): Promise<any> {
836+
try {
837+
const response = await this.upgradeService.getWorkspacesRestortable(
838+
teamId,
839+
selectedPlan,
840+
);
841+
return response;
842+
} catch (error: any) {
843+
throw new HttpException(
844+
error.message || "Failed to get unrestricted workspaces",
845+
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
846+
);
847+
}
848+
}
779849
}

src/modules/billing/payloads/stripe.payload.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
22
import {
33
downgradeUser,
44
downgradeWorkspace,
5+
upgradeWorkspace,
56
} from "@src/modules/common/models/billing.model";
67
import {
78
IsEmail,
@@ -144,6 +145,25 @@ export class CreateSubscriptionDto {
144145
@IsOptional()
145146
@IsString()
146147
promoCodeId?: string;
148+
149+
@ApiPropertyOptional({
150+
description: "List of workspaces to downgrade",
151+
example: [
152+
{ workspaceId: "workspace-id-1", name: "Workspace One" },
153+
{ workspaceId: "workspace-id-2", name: "Workspace Two" },
154+
],
155+
})
156+
@IsOptional()
157+
@IsArray()
158+
workspaces?: upgradeWorkspace[];
159+
160+
@ApiProperty({
161+
description: "Indicates if the change is an upgrade or downgrade",
162+
example: true,
163+
})
164+
@IsOptional()
165+
@IsBoolean()
166+
isUpgrade?: boolean;
147167
}
148168

149169
export class SubscriptionResponseDto {
@@ -227,6 +247,14 @@ export class UpdateSubscriptionDto {
227247
@IsOptional()
228248
@IsArray()
229249
users?: downgradeUser[];
250+
251+
@ApiProperty({
252+
description: "Indicates if the change is an upgrade or downgrade",
253+
example: true,
254+
})
255+
@IsOptional()
256+
@IsBoolean()
257+
isUpgrade?: boolean;
230258
}
231259

232260
export class CancelSubscriptionDto {

src/modules/billing/repositories/stripe-subscription.repository.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,4 +426,77 @@ export class StripeSubscriptionRepository {
426426
throw error;
427427
}
428428
}
429+
430+
/**
431+
* Add upgrade details (workspaces) to a team's upgrade record.
432+
* Automatically avoids duplicates via MongoDB's $addToSet.$each.
433+
*
434+
* @param teamId The team to update
435+
* @param workspaces Array of workspace objects {id, name}
436+
* @returns MongoDB UpdateResult
437+
*/
438+
async addUpgradeDetails(
439+
teamId: string,
440+
workspaces: Array<{ id: string; name: string }>,
441+
): Promise<void> {
442+
try {
443+
if (!teamId) {
444+
throw new Error("teamId is required to update upgrade details.");
445+
}
446+
if (!workspaces || workspaces.length === 0) {
447+
throw new Error("No workspaces provided to update upgrade details.");
448+
}
449+
const teamObjectId = new ObjectId(teamId);
450+
const updateQuery = {
451+
"upgrade.workspaces": { $each: workspaces },
452+
};
453+
const setQuery = {
454+
updatedBy: "system",
455+
updatedAt: new Date(),
456+
};
457+
await this.db.collection(Collections.TEAM).updateOne(
458+
{ _id: teamObjectId },
459+
{
460+
$addToSet: updateQuery,
461+
$set: setQuery,
462+
},
463+
);
464+
} catch (error) {
465+
console.error("Error adding upgrade details:", error);
466+
throw error;
467+
}
468+
}
469+
470+
/**
471+
* Remove upgrade workspace details from a team record.
472+
* This function removes the entire upgrade object from the team document.
473+
*
474+
* @param teamId The team to update
475+
* @param workspaceIds Array of workspace IDs to remove
476+
* @returns MongoDB UpdateResult
477+
*/
478+
async removeUpgradeDetails(teamId: string): Promise<UpdateResult> {
479+
try {
480+
if (!teamId) {
481+
throw new Error("teamId is required to remove downgrade details.");
482+
}
483+
const teamObjectId = new ObjectId(teamId);
484+
const result = await this.db.collection(Collections.TEAM).updateOne(
485+
{ _id: teamObjectId },
486+
{
487+
$unset: {
488+
upgrade: "", // Removes the entire upgrade object
489+
},
490+
$set: {
491+
updatedBy: "system",
492+
updatedAt: new Date(),
493+
},
494+
},
495+
);
496+
return result;
497+
} catch (error) {
498+
console.error("Error removing upgrade details:", error);
499+
throw error;
500+
}
501+
}
429502
}

0 commit comments

Comments
 (0)