File

apps/recallassess/recallassess-api/src/api/admin/company/company.controller.ts

Prefix

api/admin/company

Extends

Index

Methods

Methods

Async add
add()
Decorators :
@Post()
@HttpCode(HttpStatus.METHOD_NOT_ALLOWED)
Returns : Promise<never>
getStripeConnection
getStripeConnection()
Decorators :
@Get('stripe-connection')

Static route must be registered before :id/... so "stripe-connection" is not parsed as an id.

Async getStripeSubscription
getStripeSubscription(id: number)
Decorators :
@Get(':id/stripe-subscription')
Parameters :
Name Type Optional
id number No
Private normalizeFromDate
normalizeFromDate(input: unknown)
Parameters :
Name Type Optional
input unknown No
Returns : string
Private normalizeNextBillingDate
normalizeNextBillingDate(input: unknown)
Parameters :
Name Type Optional
input unknown No
Returns : string
Async postStripeCreateSubscription
postStripeCreateSubscription(id: number)
Decorators :
@Post(':id/stripe-create-subscription')
@HttpCode(HttpStatus.OK)
Parameters :
Name Type Optional
id number No
Returns : Promise<literal type>
Async postStripeNextBillingDate
postStripeNextBillingDate(id: number, body: CompanyNextBillingDateDto | literal type)
Decorators :
@Post(':id/stripe-next-billing-date')
@HttpCode(HttpStatus.OK)
Parameters :
Name Type Optional
id number No
body CompanyNextBillingDateDto | literal type No
Returns : Promise<literal type>
Async postStripeReactivateSubscription
postStripeReactivateSubscription(id: number, body: CompanyReactivateSubscriptionDto | literal type)
Decorators :
@Post(':id/stripe-reactivate-subscription')
@HttpCode(HttpStatus.OK)
Parameters :
Name Type Optional
id number No
body CompanyReactivateSubscriptionDto | literal type No
Returns : Promise<literal type>
Async postStripeReactivateSubscriptionPreview
postStripeReactivateSubscriptionPreview(id: number, body: CompanyReactivateSubscriptionDto | literal type)
Decorators :
@Post(':id/stripe-reactivate-subscription-preview')
@HttpCode(HttpStatus.OK)
Parameters :
Name Type Optional
id number No
body CompanyReactivateSubscriptionDto | literal type No
Returns : Promise<literal type>
Async postStripeSyncInvoices
postStripeSyncInvoices(id: number)
Decorators :
@Post(':id/stripe-sync-invoices')
@HttpCode(HttpStatus.OK)
Parameters :
Name Type Optional
id number No
Returns : Promise<literal type>
import { BaseController } from "@bish-nest/core/controller/base.controller";
import {
  Body,
  BadRequestException,
  Controller,
  Get,
  HttpCode,
  HttpStatus,
  MethodNotAllowedException,
  Param,
  ParseIntPipe,
  Post,
} from "@nestjs/common";
import { CompanyReactivateSubscriptionDto } from "./dto/company-reactivate-subscription.dto";
import { CompanyNextBillingDateDto } from "./dto/company-next-billing-date.dto";
import { CompanyStripeConnectionDto } from "./dto/company-stripe-connection.dto";
import { CompanyStripeSubscriptionDto } from "./dto/company-stripe-subscription.dto";
import { CompanyService } from "./services/company.service";

@Controller("api/admin/company")
export class CompanyController extends BaseController<CompanyService> {
  private normalizeFromDate(input: unknown): string {
    if (input instanceof Date && !Number.isNaN(input.getTime())) {
      return input.toISOString().slice(0, 10);
    }
    if (typeof input === "string") {
      const v = input.trim();
      if (v.length === 0) {
        throw new BadRequestException("from_date is required");
      }
      if (/^\d{4}-\d{2}-\d{2}$/.test(v)) {
        return v;
      }
      const parsed = new Date(v);
      if (!Number.isNaN(parsed.getTime())) {
        return parsed.toISOString().slice(0, 10);
      }
    }
    throw new BadRequestException("from_date must be in YYYY-MM-DD format");
  }

  private normalizeNextBillingDate(input: unknown): string {
    if (input instanceof Date && !Number.isNaN(input.getTime())) {
      return input.toISOString().slice(0, 10);
    }
    if (typeof input === "string") {
      const v = input.trim();
      if (v.length === 0) {
        throw new BadRequestException("next_billing_date is required");
      }
      if (/^\d{4}-\d{2}-\d{2}$/.test(v)) {
        return v;
      }
      const parsed = new Date(v);
      if (!Number.isNaN(parsed.getTime())) {
        return parsed.toISOString().slice(0, 10);
      }
    }
    throw new BadRequestException("next_billing_date must be in YYYY-MM-DD format");
  }

  constructor(private readonly companyService: CompanyService) {
    super(companyService);
  }

  /** Static route must be registered before `:id/...` so "stripe-connection" is not parsed as an id. */
  @Get("stripe-connection")
  getStripeConnection(): CompanyStripeConnectionDto {
    return this.companyService.getStripeConnectionDiagnostics();
  }

  @Get(":id/stripe-subscription")
  async getStripeSubscription(
    @Param("id", ParseIntPipe) id: number,
  ): Promise<CompanyStripeSubscriptionDto> {
    return this.companyService.getStripeSubscriptionSnapshot(id);
  }

  @Post(":id/stripe-create-subscription")
  @HttpCode(HttpStatus.OK)
  async postStripeCreateSubscription(
    @Param("id", ParseIntPipe) id: number,
  ): Promise<{ success: boolean; message: string }> {
    return this.companyService.runAdminCreateStripeSubscription(id);
  }

  @Post(":id/stripe-reactivate-subscription")
  @HttpCode(HttpStatus.OK)
  async postStripeReactivateSubscription(
    @Param("id", ParseIntPipe) id: number,
    @Body() body: CompanyReactivateSubscriptionDto | { from_date?: unknown },
  ): Promise<{ success: boolean; message: string }> {
    const fromDate = this.normalizeFromDate((body as { from_date?: unknown })?.from_date);
    return this.companyService.runAdminReactivateStripeSubscription(id, fromDate);
  }

  @Post(":id/stripe-reactivate-subscription-preview")
  @HttpCode(HttpStatus.OK)
  async postStripeReactivateSubscriptionPreview(
    @Param("id", ParseIntPipe) id: number,
    @Body() body: CompanyReactivateSubscriptionDto | { from_date?: unknown },
  ): Promise<{
    currency: "USD";
    base_amount: number;
    processing_fee: number;
    vat_fee: number;
    immediate_charge_amount: number;
    next_recurring_charge_amount: number;
    vip_offer_applied: boolean;
    vip_offer_summary: string | null;
    billing_cycle: "QUARTERLY" | "HALF_YEARLY" | "ANNUAL";
    from_date: string;
    end_date: string;
    next_stripe_billing_date: string;
  }> {
    const fromDate = this.normalizeFromDate((body as { from_date?: unknown })?.from_date);
    return this.companyService.previewAdminReactivateStripeSubscription(id, fromDate);
  }

  @Post(":id/stripe-next-billing-date")
  @HttpCode(HttpStatus.OK)
  async postStripeNextBillingDate(
    @Param("id", ParseIntPipe) id: number,
    @Body() body: CompanyNextBillingDateDto | { next_billing_date?: unknown },
  ): Promise<{ success: boolean; message: string; next_billing_date: string }> {
    const nextBillingDate = this.normalizeNextBillingDate(
      (body as { next_billing_date?: unknown })?.next_billing_date,
    );
    return this.companyService.updateAdminStripeNextBillingDate(id, nextBillingDate);
  }

  @Post(":id/stripe-sync-invoices")
  @HttpCode(HttpStatus.OK)
  async postStripeSyncInvoices(
    @Param("id", ParseIntPipe) id: number,
  ): Promise<{ success: boolean; message: string; invoices_created: number; refunds_created: number }> {
    return this.companyService.syncStripeInvoicesAndRefunds(id);
  }

  // Override add method - companies are created via signup, not through admin panel
  @Post()
  @HttpCode(HttpStatus.METHOD_NOT_ALLOWED)
  async add(): Promise<never> {
    throw new MethodNotAllowedException("Company creation is not allowed through the admin panel. Companies are created via the signup process.");
  }
}

results matching ""

    No results matching ""