propertyRepository = $propertyRepository; $this->bookingRepository = $bookingRepository; $this->propertyInvoiceRepository = $propertyInvoiceRepository; $commissionChannelCategoryMapping = [ 2 => 'commission_offline', 3 => 'commission', 4 => 'commission_channel', ]; $this->commissionChannelCategoryMapping = $commissionChannelCategoryMapping; $this->exchangeCurrencyCode = 'EUR'; } public function calculatePeriodCommission($propertyId, $periodStartDate, $periodFinishDate) { $propertyDetailCriteria = [ 'criteria' => [ ['field' => 'id', 'condition' => '=', 'value' => $propertyId], //['field' => 'status', 'condition' => '=', 'value' => 1], ], 'with' => ['propertyBookingEngines.channel'], 'firstRow' => true ]; $propertyDetail = $this->propertyRepository->findByCriteria($propertyDetailCriteria); $channelBaseCommission = []; foreach ($propertyDetail['property_booking_engines'] as $propertyBookingEngine) { $commissionRateParam = isset($this->commissionChannelCategoryMapping[$propertyBookingEngine['channel']['channel_category_id']]) ? $this->commissionChannelCategoryMapping[$propertyBookingEngine['channel']['channel_category_id']] : 'commission'; $commissionRateRate = $propertyDetail[$commissionRateParam]; $bookingListCriteria = [ 'criteria' => [ ['field' => 'property_id', 'condition' => '=', 'value' => $propertyBookingEngine['property_id']], ['field' => 'channel_id', 'condition' => '=', 'value' => $propertyBookingEngine['channel_id']], ['field' => 'checkout_date', 'condition' => '>=', 'value' => $periodStartDate], ['field' => 'checkout_date', 'condition' => '<', 'value' => $periodFinishDate], ], 'whereIn' => [ ['field' => 'status', 'value' => [1, 3]] ] ]; $bookingList = $this->bookingRepository->findByCriteria($bookingListCriteria, ['id', 'property_id', 'channel_id', 'total', 'currency_code', 'checkin_date', 'checkout_date']); $bookingListCollect = collect($bookingList)->groupBy('currency_code')->map(function ($group) { return [ 'total' => $group->sum('total') ]; }); $bookingListCollect = $bookingListCollect ? $bookingListCollect->toArray() : []; $exchangeRate = 1; $exchangeRateOriginal = 1; $exchangeRateDate = Carbon::parse($periodStartDate)->lastOfMonth()->toDateString(); foreach ($bookingListCollect as $bookingCurrency => $bookingTotal) { if ($bookingCurrency != $this->exchangeCurrencyCode) { $exchangeRateCheck = CurrencyRates::where('currency_code', $bookingCurrency) ->where('exc_currency_code', $this->exchangeCurrencyCode) ->where('date', $exchangeRateDate) ->first(); if ($exchangeRateCheck) { $exchangeRateCheck = $exchangeRateCheck->toArray(); } else { $exchangeRateCheck = CurrencyRates::where('currency_code', $bookingCurrency) ->where('exc_currency_code', $this->exchangeCurrencyCode) ->where('date', '<', $exchangeRateDate) ->orderBy('date', 'DESC') ->first()->toArray(); } $exchangeRate = $exchangeRateCheck['rate']; $exchangeRateDate = $exchangeRateCheck['date']; } $bookingTotalWithExchange = $bookingTotal['total'] * $exchangeRate; $bookingCommissionWithExchange = $bookingTotalWithExchange * $commissionRateRate / 100; $channelBaseCommission[$propertyBookingEngine['channel_id']][$bookingCurrency] = [ 'total' => (float)number_format($bookingTotal['total'], '4', '.', ''), 'exchangeRate' => $exchangeRate, 'exchangeRateDate' => $exchangeRateDate, 'exchangeData' => [ 'total' => (float)number_format($bookingTotalWithExchange, '4', '.', ''), 'commission' => (float)number_format($bookingCommissionWithExchange, '4', '.', ''), 'commissionRate' => $commissionRateRate, 'currencyCode' => $this->exchangeCurrencyCode, ] ]; } } $channelBaseTotal = 0; $channelBaseTotalCommission = 0; foreach ($channelBaseCommission as $channelBase) { $channelBaseValue = reset($channelBase); $channelBaseTotal += $channelBaseValue['exchangeData']['total']; $channelBaseTotalCommission += $channelBaseValue['exchangeData']['commission']; } $commission = [ 'channelBaseTotal' => $channelBaseTotal, 'channelBaseTotalCommission' => $channelBaseTotalCommission, ]; return $commission; } public function invoiceWithPeriod($propertyId, $period) { $response = ['status' => -1, 'message' => '', 'data' => null]; $excludeTaxPropertyIds = [1683]; //$propertyDetail['country'] $exchangeCurrencyCode = 'EUR'; $periodStartDate = Carbon::parse($period . '-01')->toDateString(); $periodFinishDate = Carbon::parse($periodStartDate)->addMonth()->firstOfMonth()->toDateString(); $periodFixedValueStartDate = Carbon::parse($period . '-01')->toDateString(); $periodFixedValueFinishDate = Carbon::parse($periodStartDate)->addMonth()->firstOfMonth()->toDateString(); try { $propertyDetailCriteria = [ 'criteria' => [ ['field' => 'id', 'condition' => '=', 'value' => $propertyId], //['field' => 'status', 'condition' => '=', 'value' => 1], ], 'with' => ['propertyBookingEngines.channel'], 'firstRow' => true ]; $propertyDetail = $this->propertyRepository->findByCriteria($propertyDetailCriteria); if (!$propertyDetail) { throw new ApiErrorException('Property not found'); } if ($propertyDetail['invoice_start_date']) { $propertyCreateTime = Carbon::parse($propertyDetail['invoice_start_date'])->toDateString(); } elseif ($propertyDetail['invoice_start_date']) { $propertyCreateTime = Carbon::parse($propertyDetail['activation_date'])->toDateString(); } //$propertyCreateTime = '2024-08-10'; if ($propertyCreateTime >= $periodFinishDate) { throw new ApiErrorException('Property not ready for invoice: ' . $propertyCreateTime . ' - ' . $propertyDetail['name']); } $conditions = [ 'invoice_type' => $propertyDetail['invoice_type'], 'fee' => $propertyDetail['fee'], 'credit' => $propertyDetail['credit'], 'commission' => $propertyDetail['commission'], 'commission_payment' => $propertyDetail['commission_payment'], 'commission_offline' => $propertyDetail['commission_offline'], 'commission_channel' => $propertyDetail['commission_channel'], ]; $periodCommission = $this->calculatePeriodCommission($propertyId, $periodStartDate, $periodFinishDate); if ($propertyDetail['invoice_type'] == 'ANN') { $periodYearCount = (Carbon::parse($propertyCreateTime)->lastOfMonth()->diffInMonths($periodFinishDate) % 12) + 1; $periodAnnuallyInvoiceDate = Carbon::parse($propertyCreateTime)->addMonth($periodYearCount * 12)->toDateString(); $periodFixedValueFinishDate = Carbon::parse($propertyCreateTime)->addMonths($periodYearCount * 12)->firstOfMonth()->toDateString(); $periodFixedValueStartDate = Carbon::parse($periodFixedValueFinishDate)->subMonths($periodYearCount * 12)->firstOfMonth()->toDateString(); $periodCommissionFroFixed = $this->calculatePeriodCommission($propertyId, $periodFixedValueStartDate, $periodFinishDate); } else { $periodCommissionFroFixed = $periodCommission; } $channelBaseTotal = $periodCommission['channelBaseTotal']; $channelBaseTotalCommission = $periodCommission['channelBaseTotalCommission']; if(isset($propertyDetail['promotion_fee']) && !empty($propertyDetail['promotion_fee']) && !empty($propertyDetail['promotion_fee_month'])) { if($periodFinishDate < Carbon::parse($propertyDetail['activation_date'])->addMonths($propertyDetail['promotion_fee_month'])->toDateString()) { $propertyDetail['fee'] = $propertyDetail['promotion_fee']; } } $fixedValue = 0; //$propertyDetail['fee'] if ($propertyDetail['invoice_type'] == 'MNT') { //if ($propertyDetail['credit'] == 0) { $fixedValue = $propertyDetail['fee']; //} else if ($periodCommissionFroFixed['channelBaseTotalCommission'] < $propertyDetail['credit']) { //$fixedValue = $propertyDetail['fee']; //} $isPropertyFirstMonth = Carbon::parse($propertyCreateTime)->format('Y-m') == Carbon::parse($periodStartDate)->format('Y-m') ? true : false; if ($isPropertyFirstMonth) { $propertyFirstMonthDiffInDays = Carbon::parse($propertyCreateTime)->diffInDays(Carbon::parse($periodFinishDate)->subDay()); $perDailyFixedValue = $fixedValue / Carbon::parse($periodStartDate)->daysInMonth; $fixedValue = $propertyFirstMonthDiffInDays * $perDailyFixedValue; } } elseif ($propertyDetail['invoice_type'] == 'ANN') { if (Carbon::parse($periodFixedValueStartDate)->format('Y-m') == $period) { $fixedValue = $propertyDetail['fee']; } } if ($periodCommissionFroFixed['channelBaseTotal'] < $propertyDetail['credit']) { $channelBaseTotalCommission = 0; } else { $channelBaseTotalCommission = ($periodCommissionFroFixed['channelBaseTotal'] - $propertyDetail['credit']) * $propertyDetail['commission'] / 100; } //Suspended if($propertyDetail['status'] == 3) { $fixedValue = 0; $channelBaseTotalCommission = ($periodCommissionFroFixed['channelBaseTotal']) * $propertyDetail['commission'] / 100;; } $fixedTaxValue = $fixedValue * 0.20; if (in_array($propertyDetail['id'], $excludeTaxPropertyIds)) { $fixedTaxValue = 0; } $totalInvoiceValue = $fixedValue + $fixedTaxValue + $channelBaseTotalCommission; $propertyInvoiceCreateParam = [ 'property_id' => $propertyId, 'period' => $period, 'fixed' => (float)number_format($fixedValue, '2', '.', ''), 'fixed_tax' => (float)number_format($fixedTaxValue, '2', '.', ''), 'commission' => (float)number_format($channelBaseTotalCommission, '2', '.', ''), 'total' => (float)number_format($totalInvoiceValue, '2', '.', ''), 'currency' => 'EUR', 'sales' => (float)number_format($channelBaseTotal, '2', '.', ''), 'summary' => !empty($channelBaseCommission) ? json_encode($channelBaseCommission) : null, 'conditions' => json_encode($conditions), 'status' => 1, ]; $propertyInvoiceCheckCriteria = [ 'criteria' => [ ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId], ['field' => 'period', 'condition' => '=', 'value' => $period], ], 'firstRow' => true ]; $propertyInvoiceCheck = $this->propertyInvoiceRepository->findByCriteria($propertyInvoiceCheckCriteria); if ($propertyInvoiceCheck) { $propertyInvoiceCreate = $this->propertyInvoiceRepository->update($propertyInvoiceCheck['id'], $propertyInvoiceCreateParam); } else { $propertyInvoiceCreate = $this->propertyInvoiceRepository->create($propertyInvoiceCreateParam); } if ($propertyInvoiceCreate['status'] != 'success') { throw new ApiErrorException($propertyInvoiceCreate['message']); } $response = [ 'status' => true, 'data' => $propertyInvoiceCreate['data'], ]; } catch (ApiErrorException $e) { $response['message'] = $e->getMessage(); } catch (Exception $e) { $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage(); Log::error($message); $response['message'] = $e->getMessage(); } return output($response); } }