Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions app/app/Helpers/RabbitMQHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ public static function dispatchWorkspaceSuspended($workspace, $suspension, $owne
* Dispatches the 'immediate' billing task.
* $amount should be the prorated value calculated via MainHelper.
*/
public static function dispatchImmediateBilling($workspace, $subscription, $user, $servicePlan, $billingCycle, $amount)
public static function dispatchImmediateBilling($workspace, $subscription, $user, $servicePlan, $billingCycle, $amount, $nextBillingDate)
{
$payload = [
'run_id' => 'signup_' . $user->id . '_' . time(),
Expand All @@ -136,7 +136,8 @@ public static function dispatchImmediateBilling($workspace, $subscription, $user
'creator_id' => (int) $user->id,
'action' => 'IMMEDIATE',
'amount' => $amount,
'plan_to_bill' => (int) $servicePlan->id
'plan_to_bill' => (int) $servicePlan->id,
'next_billing_date' => $nextBillingDate
];

return self::publish('billing_tasks', $payload);
Expand Down
14 changes: 14 additions & 0 deletions app/app/Http/Controllers/Admin/CustomizationsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,20 @@ public function save(Request $request)
}
$update_params['admin_portal_logo'] = $admin_logo;
}
$is_trial_enabled = false;
if ( !empty( $update_params['is_trial_enabled'] ) ) {
if ( $update_params['billing_flow'] === 'ANNUAL' ) {
$session = $request->session();
$session->flash('type', 'error');
$session->flash('message', 'Trial mode cannot be enabled with ANNUAL billing flow');
return redirect("/admin/customizations");
}
if ( $update_params['billing_flow'] === 'ANNIVERSARY' ) {
$is_trial_enabled = true;
}
}
$update_params['is_trial_enabled'] = $is_trial_enabled;

$payments_enabled = false;

if ( $update_params['payment_gateway_enabled'] =='yes') {
Expand Down
1 change: 1 addition & 0 deletions app/app/Http/Controllers/Admin/ServicePlanController.php
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ private function getFeatureOptions() {
$this->createFeatureOption('featured_plan'),
$this->createFeatureOption('pay_as_you_go'),
$this->createFeatureOption('include_in_pricing_pages'),
$this->createFeatureOption('free_trial_exempt'),
];
}

Expand Down
110 changes: 108 additions & 2 deletions app/app/Http/Controllers/MergedController.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
use League\Fractal\Resource\Collection;
use App\Transformers\RecordingTransformer;
use App\Transformers\CallTransformer;
use App\Enums\WorkspaceUserStatus;

use App\UserCredit;
use DateTime;
Expand Down Expand Up @@ -760,7 +761,8 @@ public function acceptWorkspaceInvite(Request $request) {
$workspace = Workspace::findOrFail($workspaceUser->workspace_id);
$workspaceUser->update([
'accepted' => TRUE,
'hash_expired' => TRUE
'hash_expired' => TRUE,
'status' => WorkspaceUserStatus::ACTIVE
]);


Expand Down Expand Up @@ -962,9 +964,113 @@ public function getServicePlans(Request $request) {
$item['benefits'] = $plan_benefits;
$results[] = $item;
}
return $this->response->array($results);

// Calculate next billing dates once
$currentDate = new \DateTime();
$billingDates = [];

// Check billing flow configuration (anniversary vs annual billing)
$customizations = CustomizationsKVStore::getRecord();
$billingFlow = $customizations['billing_flow'];

// For anniversary billing: add 1 month to current date with date clamping
if ($billingFlow === 'ANNIVERSARY') {
// Add 1 month for monthly billing
$nextMonthlyDate = clone $currentDate;
$nextMonthlyDate->add(new \DateInterval('P1M'));

// Handle date clamping: if the day doesn't exist in next month, move to end of month
$originalDay = $currentDate->format('d');
$lastDayOfMonth = $nextMonthlyDate->format('t');
if ((int)$originalDay > (int)$lastDayOfMonth) {
$nextMonthlyDate->setDate($nextMonthlyDate->format('Y'), $nextMonthlyDate->format('m'), $lastDayOfMonth);
}

$billingDates['next_monthly_billing_date'] = $nextMonthlyDate->format('Y-m-d');
$billingDates['next_monthly_billing_date_formatted'] = $nextMonthlyDate->format('M d, Y');

// Add 1 year for annual billing with same date clamping
$nextAnnualDate = clone $currentDate;
$nextAnnualDate->add(new \DateInterval('P1Y'));

// Handle date clamping for leap years (Feb 29)
$originalDay = $currentDate->format('d');
$lastDayOfMonth = $nextAnnualDate->format('t');
if ((int)$originalDay > (int)$lastDayOfMonth) {
$nextAnnualDate->setDate($nextAnnualDate->format('Y'), $nextAnnualDate->format('m'), $lastDayOfMonth);
}

// Apply trial period to billing dates if enabled and plan is not exempt
$isTrialEnabled = $customizations['is_trial_enabled'];
$trialDurationDays = $customizations['trial_duration_days'];

$billingDates['next_annual_billing_date'] = $nextAnnualDate->format('Y-m-d');
$billingDates['next_annual_billing_date_formatted'] = $nextAnnualDate->format('M d, Y');
$billingDates['next_monthly_billing_date'] = $nextMonthlyDate->format('Y-m-d');
$billingDates['next_monthly_billing_date_formatted'] = $nextMonthlyDate->format('M d, Y');


if ($isTrialEnabled && $trialDurationDays > 0) {
// Add trial duration to billing dates
$trialInterval = new \DateInterval('P' . $trialDurationDays . 'D');

$nextMonthlyDateWithTrial = clone $currentDate;
$nextMonthlyDateWithTrial->add(new \DateInterval('P1M'));

// Handle date clamping for monthly
$originalDay = $currentDate->format('d');
$lastDayOfMonth = $nextMonthlyDateWithTrial->format('t');
if ((int)$originalDay > (int)$lastDayOfMonth) {
$nextMonthlyDateWithTrial->setDate($nextMonthlyDateWithTrial->format('Y'), $nextMonthlyDateWithTrial->format('m'), $lastDayOfMonth);
}

$nextMonthlyDateWithTrial->add($trialInterval);
$billingDates['next_monthly_billing_date_w_trial'] = $nextMonthlyDateWithTrial->format('Y-m-d');
$billingDates['next_monthly_billing_date_w_trial_formatted'] = $nextMonthlyDateWithTrial->format('M d, Y');

$nextAnnualDateWithTrial = clone $currentDate;
$nextAnnualDateWithTrial->add(new \DateInterval('P1Y'));

// Handle date clamping for annual (leap years)
$originalDay = $currentDate->format('d');
$lastDayOfMonth = $nextAnnualDateWithTrial->format('t');
if ((int)$originalDay > (int)$lastDayOfMonth) {
$nextAnnualDateWithTrial->setDate($nextAnnualDateWithTrial->format('Y'), $nextAnnualDateWithTrial->format('m'), $lastDayOfMonth);
}

$nextAnnualDateWithTrial->add($trialInterval);
$billingDates['next_annual_billing_date_w_trial'] = $nextAnnualDateWithTrial->format('Y-m-d');
$billingDates['next_annual_billing_date_w_trial_formatted'] = $nextAnnualDateWithTrial->format('M d, Y');

}
} else {
// Monthly billing: set to first day of next month
$nextMonthlyDate = clone $currentDate;
$nextMonthlyDate->add(new \DateInterval('P1M'));
$nextMonthlyDate->setDate($nextMonthlyDate->format('Y'), $nextMonthlyDate->format('m'), 1);
$billingDates['next_monthly_billing_date'] = $nextMonthlyDate->format('Y-m-d');
$billingDates['next_monthly_billing_date_formatted'] = $nextMonthlyDate->format('M d, Y');

// Annual billing: set to first day of next year
$nextAnnualDate = clone $currentDate;
$nextAnnualDate->add(new \DateInterval('P1Y'));
$nextAnnualDate->setDate($nextAnnualDate->format('Y'), 1, 1);
$billingDates['next_annual_billing_date'] = $nextAnnualDate->format('Y-m-d');
$billingDates['next_annual_billing_date_formatted'] = $nextAnnualDate->format('M d, Y');
}



$data = [
'plans' => $results,
'billing_dates' => $billingDates,
'trial_duration_days' => $trialDurationDays
];


return $this->response->array($data);
}

public function search(Request $request) {
$query = $request->get("query");
$result = PortalSearchHelper::search( $query );
Expand Down
Loading
Loading