330 lines
14 KiB
PHP
330 lines
14 KiB
PHP
<?php
|
|
|
|
|
|
namespace App\Core\Service;
|
|
|
|
|
|
use App\Core\Repository\Booking\BookingRepository;
|
|
use App\Core\Repository\PropertyInvoice\PropertyInvoiceRepository;
|
|
use App\Core\Repository\Property\PropertyRepository;
|
|
use App\Exceptions\ApiErrorException;
|
|
use App\Models\CurrencyRates;
|
|
use Carbon\Carbon;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class PropertyInvoiceService
|
|
{
|
|
private $countryRepository;
|
|
|
|
public function __construct(
|
|
PropertyRepository $propertyRepository,
|
|
BookingRepository $bookingRepository,
|
|
PropertyInvoiceRepository $propertyInvoiceRepository
|
|
)
|
|
{
|
|
$this->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);
|
|
}
|
|
|
|
}
|