feat: Add free/exempt subscription status for organizations
- Add 'free' subscription status (exempt from fees) - POST /admin/organizations/:id/set-free - Set org as free - POST /admin/organizations/:id/remove-free - Remove free status - Free orgs get enterprise plan, no expiry
This commit is contained in:
@@ -38,6 +38,11 @@ export async function subscriptionMiddleware(ctx: Context, next: Next): Promise<
|
|||||||
let message = "";
|
let message = "";
|
||||||
|
|
||||||
switch (org.subscription_status) {
|
switch (org.subscription_status) {
|
||||||
|
case "free":
|
||||||
|
// Exempt from fees - always active
|
||||||
|
isActive = true;
|
||||||
|
break;
|
||||||
|
|
||||||
case "active":
|
case "active":
|
||||||
// Check if subscription has ended
|
// Check if subscription has ended
|
||||||
if (org.subscription_ends_at && new Date(org.subscription_ends_at) < now) {
|
if (org.subscription_ends_at && new Date(org.subscription_ends_at) < now) {
|
||||||
|
|||||||
@@ -369,6 +369,49 @@ adminRouter.post("/organizations/:id/extend-trial", requireSuperAdmin, async (ct
|
|||||||
ctx.response.body = { message: `Trial um ${extendDays} Tage verlängert` };
|
ctx.response.body = { message: `Trial um ${extendDays} Tage verlängert` };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Set organization as free (exempt from fees)
|
||||||
|
adminRouter.post("/organizations/:id/set-free", requireSuperAdmin, async (ctx) => {
|
||||||
|
const orgId = ctx.params.id;
|
||||||
|
const body = await ctx.request.body.json();
|
||||||
|
const { reason } = body;
|
||||||
|
|
||||||
|
await execute(
|
||||||
|
`UPDATE organizations SET
|
||||||
|
subscription_status = 'free',
|
||||||
|
subscription_plan = 'enterprise',
|
||||||
|
subscription_ends_at = NULL,
|
||||||
|
trial_ends_at = NULL,
|
||||||
|
subscription_paused_at = NULL,
|
||||||
|
subscription_pause_reason = NULL,
|
||||||
|
settings = settings || $1::jsonb
|
||||||
|
WHERE id = $2`,
|
||||||
|
[JSON.stringify({ free_reason: reason || "Freigestellt durch Admin", free_since: new Date().toISOString() }), orgId]
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.response.body = { message: "Organisation freigestellt (kostenlos)" };
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove free status (back to trial or active)
|
||||||
|
adminRouter.post("/organizations/:id/remove-free", requireSuperAdmin, async (ctx) => {
|
||||||
|
const orgId = ctx.params.id;
|
||||||
|
const body = await ctx.request.body.json();
|
||||||
|
const { set_trial_days } = body;
|
||||||
|
|
||||||
|
const trialDays = set_trial_days || 14;
|
||||||
|
|
||||||
|
await execute(
|
||||||
|
`UPDATE organizations SET
|
||||||
|
subscription_status = 'trial',
|
||||||
|
subscription_plan = 'starter',
|
||||||
|
trial_ends_at = NOW() + INTERVAL '${trialDays} days',
|
||||||
|
settings = settings - 'free_reason' - 'free_since'
|
||||||
|
WHERE id = $1`,
|
||||||
|
[orgId]
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.response.body = { message: `Freistellung aufgehoben, ${trialDays} Tage Trial gesetzt` };
|
||||||
|
});
|
||||||
|
|
||||||
// List all organizations with subscription info
|
// List all organizations with subscription info
|
||||||
adminRouter.get("/subscriptions", requireSuperAdmin, async (ctx) => {
|
adminRouter.get("/subscriptions", requireSuperAdmin, async (ctx) => {
|
||||||
const filter = ctx.request.url.searchParams.get("filter"); // all, trial, active, paused, expired
|
const filter = ctx.request.url.searchParams.get("filter"); // all, trial, active, paused, expired
|
||||||
|
|||||||
Reference in New Issue
Block a user