add optinal apps
This commit is contained in:
@@ -10,7 +10,9 @@ import {
|
||||
CreateServicePlanDto,
|
||||
UpdateServicePlanDto,
|
||||
CalculateCostDto,
|
||||
UpgradeResourcesDto,
|
||||
} from './dto/billing.dto';
|
||||
import { Application } from '../applications/entities/application.entity';
|
||||
|
||||
@Injectable()
|
||||
export class BillingService {
|
||||
@@ -133,9 +135,14 @@ export class BillingService {
|
||||
// Parse resource values
|
||||
const cpuCores = this.parseCpuToCores(dto.cpuLimit);
|
||||
const memoryGb = this.parseMemoryToGb(dto.memoryLimit);
|
||||
const storageGb = dto.dbStorageSize ? parseFloat(dto.dbStorageSize.replace('Gi', '')) || 0 : 0;
|
||||
const dbStorageGb = dto.dbStorageSize ? parseFloat(dto.dbStorageSize.replace('Gi', '')) || 0 : 0;
|
||||
const appStorageGb = dto.appStorageSize ? parseFloat(dto.appStorageSize.replace('Gi', '')) || 0 : 0;
|
||||
const totalStorageGb = dbStorageGb + appStorageGb;
|
||||
const hasDatabase = dto.databaseType !== 'none';
|
||||
const replicas = dto.replicas || 1;
|
||||
const hasRedis = dto.enableRedis || false;
|
||||
const hasRabbitmq = dto.enableRabbitmq || false;
|
||||
const hasElasticsearch = dto.enableElasticsearch || false;
|
||||
|
||||
const breakdown: { label: string; hourly: number; monthly: number; yearly: number }[] = [];
|
||||
let totalBase = 0;
|
||||
@@ -158,13 +165,25 @@ export class BillingService {
|
||||
label = `Memory (${(memoryGb * replicas).toFixed(2)} GB)`;
|
||||
break;
|
||||
case PricingResourceType.STORAGE_PER_GB:
|
||||
cost = storageGb * Number(rule.unitPrice);
|
||||
label = `Storage (${storageGb} GB)`;
|
||||
cost = totalStorageGb * Number(rule.unitPrice);
|
||||
label = `Storage (${totalStorageGb} GB)`;
|
||||
break;
|
||||
case PricingResourceType.DATABASE_ADDON:
|
||||
cost = hasDatabase ? Number(rule.unitPrice) : 0;
|
||||
label = 'Database addon';
|
||||
break;
|
||||
case PricingResourceType.REDIS_ADDON:
|
||||
cost = hasRedis ? Number(rule.unitPrice) : 0;
|
||||
label = 'Redis addon';
|
||||
break;
|
||||
case PricingResourceType.RABBITMQ_ADDON:
|
||||
cost = hasRabbitmq ? Number(rule.unitPrice) : 0;
|
||||
label = 'RabbitMQ addon';
|
||||
break;
|
||||
case PricingResourceType.ELASTICSEARCH_ADDON:
|
||||
cost = hasElasticsearch ? Number(rule.unitPrice) : 0;
|
||||
label = 'Elasticsearch addon';
|
||||
break;
|
||||
}
|
||||
|
||||
if (cost > 0) {
|
||||
@@ -300,6 +319,7 @@ export class BillingService {
|
||||
memoryLimit: string;
|
||||
replicas: number;
|
||||
dbStorageSize?: string;
|
||||
appStorageSize?: string;
|
||||
}): Promise<{ hourly: number; monthly: number; yearly: number }> {
|
||||
const result = await this.calculateCost({
|
||||
runtime: app.runtime,
|
||||
@@ -308,7 +328,100 @@ export class BillingService {
|
||||
memoryLimit: app.memoryLimit,
|
||||
replicas: app.replicas,
|
||||
dbStorageSize: app.dbStorageSize,
|
||||
appStorageSize: app.appStorageSize,
|
||||
});
|
||||
return { hourly: result.hourly, monthly: result.monthly, yearly: result.yearly };
|
||||
}
|
||||
|
||||
// ─── Renewal Cost Calculation ─────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Calculate renewal cost for an application.
|
||||
* Returns cost for each billing cycle based on current app config.
|
||||
*/
|
||||
async calculateRenewalCost(app: Application): Promise<{
|
||||
hourly: number;
|
||||
monthly: number;
|
||||
yearly: number;
|
||||
currentCycle?: BillingCycle;
|
||||
currentCycleCost?: number;
|
||||
}> {
|
||||
const costs = await this.calculateCostForApp(app);
|
||||
|
||||
let currentCycleCost: number | undefined;
|
||||
if (app.billingCycle) {
|
||||
currentCycleCost = app.billingCycle === BillingCycle.HOURLY ? costs.hourly
|
||||
: app.billingCycle === BillingCycle.MONTHLY ? costs.monthly
|
||||
: costs.yearly;
|
||||
}
|
||||
|
||||
return {
|
||||
...costs,
|
||||
currentCycle: app.billingCycle,
|
||||
currentCycleCost,
|
||||
};
|
||||
}
|
||||
|
||||
// ─── Resource Upgrade Cost Calculation ────────────────────────────
|
||||
|
||||
/**
|
||||
* Calculate the cost difference for a resource upgrade.
|
||||
* Returns the additional cost per billing cycle.
|
||||
*/
|
||||
async calculateUpgradeCost(
|
||||
app: Application,
|
||||
newResources: UpgradeResourcesDto,
|
||||
): Promise<{
|
||||
currentCost: { hourly: number; monthly: number; yearly: number };
|
||||
newCost: { hourly: number; monthly: number; yearly: number };
|
||||
difference: { hourly: number; monthly: number; yearly: number };
|
||||
proratedAmount: number;
|
||||
remainingHours: number;
|
||||
billingCycle: BillingCycle | null;
|
||||
}> {
|
||||
// Current cost
|
||||
const currentCost = await this.calculateCostForApp(app);
|
||||
|
||||
// New cost with upgraded resources
|
||||
const newCost = await this.calculateCost({
|
||||
runtime: app.runtime,
|
||||
databaseType: app.databaseType,
|
||||
cpuLimit: newResources.cpuLimit || app.cpuLimit,
|
||||
memoryLimit: newResources.memoryLimit || app.memoryLimit,
|
||||
replicas: newResources.replicas ?? app.replicas,
|
||||
dbStorageSize: newResources.dbStorageSize || app.dbStorageSize,
|
||||
appStorageSize: newResources.appStorageSize || app.appStorageSize,
|
||||
});
|
||||
|
||||
// Difference
|
||||
const difference = {
|
||||
hourly: newCost.hourly - currentCost.hourly,
|
||||
monthly: newCost.monthly - currentCost.monthly,
|
||||
yearly: newCost.yearly - currentCost.yearly,
|
||||
};
|
||||
|
||||
// Calculate prorated amount based on remaining time in billing period
|
||||
let proratedAmount = 0;
|
||||
let remainingHours = 0;
|
||||
|
||||
if (app.planExpiresAt && app.billingCycle) {
|
||||
const now = new Date();
|
||||
const expiresAt = new Date(app.planExpiresAt);
|
||||
remainingHours = Math.max(0, (expiresAt.getTime() - now.getTime()) / (1000 * 60 * 60));
|
||||
|
||||
// Only charge difference if upgrading (not downgrading)
|
||||
if (difference.hourly > 0) {
|
||||
proratedAmount = Math.ceil(difference.hourly * remainingHours);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
currentCost,
|
||||
newCost,
|
||||
difference,
|
||||
proratedAmount,
|
||||
remainingHours: Math.round(remainingHours),
|
||||
billingCycle: app.billingCycle,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user