Commit a2ad6e54 authored by Mahmoud Aglan's avatar Mahmoud Aglan

Default programs to monthly billing day 1, resolve member vs non-member pricing

- ProgramForm defaults: billing_cycle='monthly', billing_day=1
- PricingService.findBasePrice now resolves correct base price by
  participant.membership_type (member → member price, else → non_member price)
- Resolution order: branch+membership > branch+any > global+membership > global
- NULL membership_type defaults to non_member pricing
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 9921dd22
......@@ -40,7 +40,7 @@ public function calculate(
$branchId ??= $this->resolveParticipantBranch($participant);
// ─── Step 1: Find base price (fail if none exists) ────────────
$basePrice = $this->findBasePrice($priceable, $branchId, $date);
$basePrice = $this->findBasePrice($priceable, $branchId, $date, $participant);
if (!$basePrice) {
throw new DomainException('لا يوجد سعر محدد');
}
......@@ -153,7 +153,7 @@ public function calculate(
// ─── Step 1: Base Price Resolution ────────────────────────────────────
private function findBasePrice(Model $priceable, ?int $branchId, string $date): ?BasePrice
private function findBasePrice(Model $priceable, ?int $branchId, string $date, ?Participant $participant = null): ?BasePrice
{
$query = BasePrice::where('priceable_type', $priceable->getMorphClass())
->where('priceable_id', $priceable->getKey())
......@@ -164,18 +164,40 @@ private function findBasePrice(Model $priceable, ?int $branchId, string $date):
->orWhere('effective_to', '>=', $date);
});
// Branch-specific first, then fallback to null (all branches)
// Resolve membership type from participant
$membershipType = $participant?->membership_type?->value ?? 'non_member';
// Try membership-type-specific price first, then fallback to any
$withMembership = fn ($q) => (clone $q)->where('metadata->membership_type', $membershipType);
// Branch-specific + membership type first
if ($branchId) {
$branchSpecific = (clone $query)
->where('branch_id', $branchId)
$match = $withMembership((clone $query)->where('branch_id', $branchId))
->orderByDesc('priority')
->first();
if ($match) {
return $match;
}
if ($branchSpecific) {
return $branchSpecific;
// Branch-specific without membership filter
$branchAny = (clone $query)
->where('branch_id', $branchId)
->orderByDesc('priority')
->first();
if ($branchAny) {
return $branchAny;
}
}
// No branch: membership type first
$match = $withMembership((clone $query)->whereNull('branch_id'))
->orderByDesc('priority')
->first();
if ($match) {
return $match;
}
// Final fallback: any active base price
return $query->whereNull('branch_id')
->orderByDesc('priority')
->first();
......
......@@ -47,8 +47,8 @@ class ProgramForm extends Component
public ?string $program_start_date = null;
public ?string $program_end_date = null;
public string $renewal_policy = 'manual_renew';
public ?string $billing_cycle = null;
public ?int $billing_day = null;
public ?string $billing_cycle = 'monthly';
public ?int $billing_day = 1;
public string $cancellation_policy = '';
public string $refund_policy = '';
public string $status = 'active';
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment